Introspection fix
[gnumeric.git] / src / gutils.c
blob4a4d01c43492749c0b3a43e7e802a716970635f9
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 gboolean
47 running_in_tree (void)
49 const char *argv0 = g_get_prgname ();
51 if (!argv0)
52 return FALSE;
54 /* Sometime we see, e.g., "lt-gnumeric" as basename. */
56 char *base = g_path_get_basename (argv0);
57 gboolean has_lt_prefix = (strncmp (base, "lt-", 3) == 0);
58 g_free (base);
59 if (has_lt_prefix)
60 return TRUE;
63 /* Look for ".libs" as final path element. */
65 const char *dotlibs = strstr (argv0, ".libs/");
66 if (dotlibs &&
67 (dotlibs == argv0 || G_IS_DIR_SEPARATOR (dotlibs[-1])) &&
68 strchr (dotlibs + 6, G_DIR_SEPARATOR) == NULL)
69 return TRUE;
72 return FALSE;
75 static gboolean gutils_inited = FALSE;
77 void
78 gutils_init (void)
80 char const *home_dir;
82 // This function will end up being called twice in normal operation:
83 // once from gnm_pre_parse_init and once from gnm_init. Introspection
84 // will not get the first.
85 if (gutils_inited)
86 return;
88 #ifdef G_OS_WIN32
89 gchar *dir = g_win32_get_package_installation_directory_of_module (NULL);
90 gnumeric_lib_dir = g_build_filename (dir, "lib",
91 "gnumeric", GNM_VERSION_FULL,
92 NULL);
93 gnumeric_data_dir = g_build_filename (dir, "share",
94 "gnumeric", GNM_VERSION_FULL,
95 NULL);
96 gnumeric_locale_dir = g_build_filename (dir, "share", "locale", NULL);
97 gnumeric_extern_plugin_dir = g_build_filename
98 (dir, "lib", "gnumeric", GNM_API_VERSION, "plugins",
99 NULL);
100 g_free (dir);
101 #else
102 if (running_in_tree ()) {
103 const char *argv0 = g_get_prgname ();
104 char *dotlibs = g_path_get_dirname (argv0);
105 char *top = g_build_filename (dotlibs, "..", "../", NULL);
106 char *plugins = g_build_filename (top, PLUGIN_SUBDIR, NULL);
107 if (g_file_test (plugins, G_FILE_TEST_IS_DIR))
108 gnumeric_lib_dir =
109 go_filename_simplify (top, GO_DOTDOT_SYNTACTIC,
110 FALSE);
111 g_free (top);
112 g_free (plugins);
113 g_free (dotlibs);
114 if (0) g_printerr ("Running in-tree\n");
117 if (!gnumeric_lib_dir)
118 gnumeric_lib_dir = g_strdup (GNUMERIC_LIBDIR);
119 gnumeric_data_dir = g_strdup (GNUMERIC_DATADIR);
120 gnumeric_locale_dir = g_strdup (GNUMERIC_LOCALEDIR);
121 gnumeric_extern_plugin_dir = g_strdup (GNUMERIC_EXTERNPLUGINDIR);
122 #endif
123 home_dir = g_get_home_dir ();
124 gnumeric_usr_dir_unversioned = home_dir
125 ? g_build_filename (home_dir, ".gnumeric", NULL)
126 : NULL;
127 gnumeric_usr_dir = gnumeric_usr_dir_unversioned
128 ? g_build_filename (gnumeric_usr_dir_unversioned, GNM_VERSION_FULL, NULL)
129 : NULL;
131 gutils_inited = TRUE;
134 void
135 gutils_shutdown (void)
137 GSList *l;
139 g_free (gnumeric_lib_dir);
140 gnumeric_lib_dir = NULL;
141 g_free (gnumeric_data_dir);
142 gnumeric_data_dir = NULL;
143 g_free (gnumeric_locale_dir);
144 gnumeric_locale_dir = NULL;
145 g_free (gnumeric_usr_dir);
146 gnumeric_usr_dir = NULL;
147 g_free (gnumeric_usr_dir_unversioned);
148 gnumeric_usr_dir_unversioned = NULL;
149 g_free (gnumeric_extern_plugin_dir);
150 gnumeric_extern_plugin_dir = NULL;
152 for (l = gutils_xml_in_docs; l; l = l->next) {
153 GsfXMLInDoc **pdoc = l->data;
154 gsf_xml_in_doc_free (*pdoc);
155 *pdoc = NULL;
157 g_slist_free (gutils_xml_in_docs);
158 gutils_xml_in_docs = NULL;
161 char const *
162 gnm_sys_lib_dir (void)
164 return gnumeric_lib_dir;
167 char const *
168 gnm_sys_data_dir (void)
170 return gnumeric_data_dir;
173 char const *
174 gnm_sys_extern_plugin_dir (void)
176 return gnumeric_extern_plugin_dir;
179 char const *
180 gnm_locale_dir (void)
182 return gnumeric_locale_dir;
185 char const *
186 gnm_usr_dir (gboolean versioned)
188 return versioned ? gnumeric_usr_dir : gnumeric_usr_dir_unversioned;
192 static gboolean
193 all_ascii (const char *s)
195 while ((guchar)*s < 0x7f) {
196 if (*s)
197 s++;
198 else
199 return TRUE;
201 return FALSE;
205 * Like strto[ld], but...
206 * 1. handles non-ascii characters
207 * 2. disallows 0x000.0p+00 and 0.0d+00
208 * 3. ensures sane errno on exit
210 gnm_float
211 gnm_utf8_strto (const char *s, char **end)
213 const char *p;
214 int sign;
215 char *dummy_end;
216 GString *ascii;
217 GString const *decimal = go_locale_get_decimal ();
218 gboolean seen_decimal = FALSE;
219 gboolean seen_digit = FALSE;
220 size_t spaces = 0;
221 gnm_float res;
222 int save_errno;
224 if (all_ascii (s)) {
225 res = gnm_strto (s, end);
226 goto handle_denormal;
229 ascii = g_string_sized_new (100);
231 if (!end)
232 end = &dummy_end;
234 p = s;
235 while (g_unichar_isspace (g_utf8_get_char (p))) {
236 p = g_utf8_next_char (p);
237 spaces++;
240 sign = go_unichar_issign (g_utf8_get_char (p));
241 if (sign) {
242 g_string_append_c (ascii, "-/+"[sign + 1]);
243 p = g_utf8_next_char (p);
246 do {
247 if (strncmp (p, decimal->str, decimal->len) == 0) {
248 if (seen_decimal)
249 break;
250 seen_decimal = TRUE;
251 go_string_append_gstring (ascii, decimal);
252 p += decimal->len;
253 } else if (g_unichar_isdigit (g_utf8_get_char (p))) {
254 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
255 p = g_utf8_next_char (p);
256 seen_digit = TRUE;
257 } else
258 break;
259 } while (1);
261 if (!seen_digit) {
262 /* No conversion, bail to gnm_strto for nan etc. */
263 g_string_free (ascii, TRUE);
264 return gnm_strto (s, end);
267 if (*p == 'e' || *p == 'E') {
268 int sign;
270 g_string_append_c (ascii, 'e');
271 p = g_utf8_next_char (p);
273 sign = go_unichar_issign (g_utf8_get_char (p));
274 if (sign) {
275 g_string_append_c (ascii, "-/+"[sign + 1]);
276 p = g_utf8_next_char (p);
278 while (g_unichar_isdigit (g_utf8_get_char (p))) {
279 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
280 p = g_utf8_next_char (p);
284 res = gnm_strto (ascii->str, end);
285 save_errno = errno;
286 *end = g_utf8_offset_to_pointer
287 (s, spaces + g_utf8_pointer_to_offset (ascii->str, *end));
288 g_string_free (ascii, TRUE);
290 errno = save_errno;
292 handle_denormal:
293 save_errno = errno;
294 if (res != 0 && gnm_abs (res) < GNM_MIN)
295 errno = 0;
296 else
297 errno = save_errno;
299 return res;
303 * Like strtol, but...
304 * 1. handles non-ascii characters
305 * 2. assumes base==10
306 * 3. ensures sane errno on exit
308 long
309 gnm_utf8_strtol (const char *s, char **end)
311 const char *p;
312 int sign;
313 char *dummy_end;
314 unsigned long res = 0, lim, limd;
316 if (!end)
317 end = &dummy_end;
319 p = s;
320 while (g_unichar_isspace (g_utf8_get_char (p)))
321 p = g_utf8_next_char (p);
323 sign = go_unichar_issign (g_utf8_get_char (p));
324 if (sign)
325 p = g_utf8_next_char (p);
326 if (sign < 0) {
327 lim = (-(unsigned long)LONG_MIN) / 10u;
328 limd = (-(unsigned long)LONG_MIN) % 10u;
329 } else {
330 lim = (unsigned long)LONG_MAX / 10u;
331 limd = (unsigned long)LONG_MAX % 10u;
334 if (!g_unichar_isdigit (g_utf8_get_char (p))) {
335 errno = 0;
336 *end = (char *)s;
337 return 0;
340 while (g_unichar_isdigit (g_utf8_get_char (p))) {
341 guint8 dig = g_unichar_digit_value (g_utf8_get_char (p));
342 p = g_utf8_next_char (p);
344 if (res > lim || (res == lim && dig > limd)) {
345 /* Overflow */
346 while (g_unichar_isdigit (g_utf8_get_char (p)))
347 p = g_utf8_next_char (p);
348 *end = (char *)p;
349 errno = ERANGE;
350 return sign < 0 ? LONG_MIN : LONG_MAX;
353 res = res * 10u + dig;
355 *end = (char *)p;
356 errno = 0;
357 return sign < 0 ? (long)-res : (long)res;
362 gnm_regcomp_XL (GORegexp *preg, char const *pattern, int cflags,
363 gboolean anchor_start, gboolean anchor_end)
365 GString *res = g_string_new (NULL);
366 int retval;
368 if (anchor_start)
369 g_string_append_c (res, '^');
371 while (*pattern) {
372 switch (*pattern) {
373 case '*':
374 g_string_append (res, ".*");
375 pattern++;
376 break;
378 case '?':
379 g_string_append_c (res, '.');
380 pattern++;
381 break;
383 case '~':
384 if (pattern[1] == '*' ||
385 pattern[1] == '?' ||
386 pattern[1] == '~')
387 pattern++;
388 /* Fall through */
389 default:
390 pattern = go_regexp_quote1 (res, pattern);
394 if (anchor_end)
395 g_string_append_c (res, '$');
397 retval = go_regcomp (preg, res->str, cflags);
398 g_string_free (res, TRUE);
399 return retval;
403 * gnm_excel_search_impl:
404 * @needle: the pattern to search for, see gnm_regcomp_XL.
405 * @haystack: the string to search in.
406 * @skip: zero-based search start point in characters.
408 * Returns: -1 for a non-match, or zero-based location in
409 * characters.
411 * The is the implementation of Excel's SEARCH function.
412 * However, note that @skip and return value are zero-based.
415 gnm_excel_search_impl (const char *needle, const char *haystack,
416 size_t skip)
418 const char *hay2;
419 size_t i;
420 GORegexp r;
422 for (i = skip, hay2 = haystack; i > 0; i--) {
423 if (*hay2 == 0)
424 return -1;
425 hay2 = g_utf8_next_char (hay2);
428 if (gnm_regcomp_XL (&r, needle, GO_REG_ICASE, FALSE, FALSE) == GO_REG_OK) {
429 GORegmatch rm;
431 switch (go_regexec (&r, hay2, 1, &rm, 0)) {
432 case GO_REG_NOMATCH:
433 break;
434 case GO_REG_OK:
435 go_regfree (&r);
436 return skip +
437 g_utf8_pointer_to_offset (hay2, hay2 + rm.rm_so);
438 default:
439 g_warning ("Unexpected go_regexec result");
441 go_regfree (&r);
442 } else {
443 g_warning ("Unexpected regcomp result");
446 return -1;
450 #if 0
451 static char const *
452 color_to_string (PangoColor color)
454 static char result[100];
455 sprintf (result, "%04x:%04x:%04x", color.red, color.green, color.blue);
456 return result;
459 static const char *
460 enum_name (GType typ, int i)
462 static char result[100];
463 GEnumClass *ec = g_type_class_ref (typ);
465 if (ec) {
466 GEnumValue *ev = g_enum_get_value (ec, i);
467 g_type_class_unref (ec);
469 if (ev && ev->value_nick)
470 return ev->value_nick;
471 if (ev && ev->value_name)
472 return ev->value_name;
475 sprintf (result, "%d", i);
476 return result;
479 static gboolean
480 cb_gnm_pango_attr_dump (PangoAttribute *attr, gpointer user_data)
482 g_print (" start=%u; end=%u\n", attr->start_index, attr->end_index);
483 switch (attr->klass->type) {
484 case PANGO_ATTR_FAMILY:
485 g_print (" family=\"%s\"\n", ((PangoAttrString *)attr)->value);
486 break;
487 case PANGO_ATTR_LANGUAGE:
488 g_print (" language=\"%s\"\n", pango_language_to_string (((PangoAttrLanguage *)attr)->value));
489 break;
490 case PANGO_ATTR_STYLE:
491 g_print (" style=%s\n",
492 enum_name (PANGO_TYPE_STYLE, ((PangoAttrInt *)attr)->value));
493 break;
494 case PANGO_ATTR_WEIGHT:
495 g_print (" weight=%s\n",
496 enum_name (PANGO_TYPE_WEIGHT, ((PangoAttrInt *)attr)->value));
497 break;
498 case PANGO_ATTR_VARIANT:
499 g_print (" variant=%s\n",
500 enum_name (PANGO_TYPE_VARIANT, ((PangoAttrInt *)attr)->value));
501 break;
502 case PANGO_ATTR_STRETCH:
503 g_print (" stretch=%s\n",
504 enum_name (PANGO_TYPE_STRETCH, ((PangoAttrInt *)attr)->value));
505 break;
506 case PANGO_ATTR_UNDERLINE:
507 g_print (" underline=%s\n",
508 enum_name (PANGO_TYPE_UNDERLINE, ((PangoAttrInt *)attr)->value));
509 break;
510 case PANGO_ATTR_STRIKETHROUGH:
511 g_print (" strikethrough=%d\n", ((PangoAttrInt *)attr)->value);
512 break;
513 case PANGO_ATTR_RISE:
514 g_print (" rise=%d\n", ((PangoAttrInt *)attr)->value);
515 break;
516 case PANGO_ATTR_FALLBACK:
517 g_print (" fallback=%d\n", ((PangoAttrInt *)attr)->value);
518 break;
519 case PANGO_ATTR_LETTER_SPACING:
520 g_print (" letter_spacing=%d\n", ((PangoAttrInt *)attr)->value);
521 break;
522 case PANGO_ATTR_SIZE:
523 g_print (" size=%d%s\n",
524 ((PangoAttrSize *)attr)->size,
525 ((PangoAttrSize *)attr)->absolute ? " abs" : "");
526 break;
527 case PANGO_ATTR_SCALE:
528 g_print (" scale=%g\n", ((PangoAttrFloat *)attr)->value);
529 break;
530 case PANGO_ATTR_FOREGROUND:
531 g_print (" foreground=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
532 break;
533 case PANGO_ATTR_BACKGROUND:
534 g_print (" background=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
535 break;
536 case PANGO_ATTR_UNDERLINE_COLOR:
537 g_print (" underline_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
538 break;
539 case PANGO_ATTR_STRIKETHROUGH_COLOR:
540 g_print (" strikethrough_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
541 break;
542 case PANGO_ATTR_FONT_DESC: {
543 char *desc = pango_font_description_to_string (((PangoAttrFontDesc*)attr)->desc);
544 g_print (" font=\"%s\"\n", desc);
545 g_free (desc);
546 break;
548 default:
549 g_print (" type=%s\n", enum_name (PANGO_TYPE_ATTR_TYPE, attr->klass->type));
552 return FALSE;
555 void
556 gnm_pango_attr_dump (PangoAttrList *list)
558 g_print ("PangoAttrList at %p\n", list);
559 pango_attr_list_filter (list, cb_gnm_pango_attr_dump, NULL);
561 #endif
564 static gboolean
565 cb_gnm_pango_attr_list_equal (PangoAttribute *a, gpointer _sl)
567 GSList **sl = _sl;
568 *sl = g_slist_prepend (*sl, a);
569 return FALSE;
573 * This is a bit of a hack. It might claim a difference even when things
574 * actually are equal. But not the other way around.
576 gboolean
577 gnm_pango_attr_list_equal (PangoAttrList const *l1, PangoAttrList const *l2)
579 if (l1 == l2)
580 return TRUE;
581 else if (l1 == NULL || l2 == NULL)
582 return FALSE;
583 else {
584 gboolean res;
585 GSList *sl1 = NULL, *sl2 = NULL;
586 (void)pango_attr_list_filter ((PangoAttrList *)l1,
587 cb_gnm_pango_attr_list_equal,
588 &sl1);
589 (void)pango_attr_list_filter ((PangoAttrList *)l2,
590 cb_gnm_pango_attr_list_equal,
591 &sl2);
593 while (sl1 && sl2) {
594 const PangoAttribute *a1 = sl1->data;
595 const PangoAttribute *a2 = sl2->data;
596 if (a1->start_index != a2->start_index ||
597 a1->end_index != a2->end_index ||
598 !pango_attribute_equal (a1, a2))
599 break;
600 sl1 = g_slist_delete_link (sl1, sl1);
601 sl2 = g_slist_delete_link (sl2, sl2);
604 res = (sl1 == sl2);
605 g_slist_free (sl1);
606 g_slist_free (sl2);
607 return res;
611 /* ------------------------------------------------------------------------- */
613 struct _GnmLocale {
614 char *num_locale;
615 char *monetary_locale;
618 * gnm_push_C_locale: (skip)
620 * Returns the current locale, and sets the locale and the value-format
621 * engine's locale to 'C'. The caller must call gnm_pop_C_locale to free the
622 * result and restore the previous locale.
624 GnmLocale *
625 gnm_push_C_locale (void)
627 GnmLocale *old = g_new0 (GnmLocale, 1);
629 old->num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
630 go_setlocale (LC_NUMERIC, "C");
631 old->monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
632 go_setlocale (LC_MONETARY, "C");
633 go_locale_untranslated_booleans ();
635 return old;
639 * gnm_pop_C_locale: (skip)
640 * @locale: #GnmLocale
642 * Frees the result of gnm_push_C_locale and restores the original locale.
644 void
645 gnm_pop_C_locale (GnmLocale *locale)
647 /* go_setlocale restores bools to locale translation */
648 go_setlocale (LC_MONETARY, locale->monetary_locale);
649 g_free (locale->monetary_locale);
650 go_setlocale (LC_NUMERIC, locale->num_locale);
651 g_free (locale->num_locale);
652 g_free (locale);
655 /* ------------------------------------------------------------------------- */
657 gboolean
658 gnm_debug_flag (const char *flag)
660 GDebugKey key;
661 key.key = (char *)flag;
662 key.value = 1;
664 return g_parse_debug_string (g_getenv ("GNM_DEBUG"), &key, 1) != 0;
667 /* ------------------------------------------------------------------------- */
669 void
670 gnm_string_add_number (GString *buf, gnm_float d)
672 size_t old_len = buf->len;
673 double d2;
674 static int digits;
676 if (digits == 0) {
677 gnm_float l10 = gnm_log10 (FLT_RADIX);
678 digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
679 (l10 == (int)l10 ? 0 : 1);
682 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits - 1, d);
683 d2 = gnm_strto (buf->str + old_len, NULL);
685 if (d != d2) {
686 g_string_truncate (buf, old_len);
687 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits, d);
691 /* ------------------------------------------------------------------------- */
693 void
694 gnm_insert_meta_date (GODoc *doc, char const *name)
696 GValue *value = g_new0 (GValue, 1);
697 GTimeVal tm;
698 GsfTimestamp *ts = gsf_timestamp_new ();
700 g_get_current_time (&tm);
701 tm.tv_usec = 0L;
703 gsf_timestamp_set_time (ts, tm.tv_sec);
704 g_value_init (value, GSF_TIMESTAMP_TYPE);
705 gsf_timestamp_to_value (ts, value);
706 gsf_timestamp_free (ts);
708 gsf_doc_meta_data_insert (go_doc_get_meta_data (doc),
709 g_strdup (name),
710 value);
713 /* ------------------------------------------------------------------------- */
716 * gnm_object_get_bool:
717 * @o: #GObject
718 * @name: property name
720 * Returns: the value of @o's boolean property @name.
722 gboolean
723 gnm_object_get_bool (gpointer o, const char *name)
725 gboolean b;
726 g_object_get (o, name, &b, NULL);
727 return b;
731 * gnm_object_has_readable_prop:
732 * @obj: #GObject
733 * @property: property name
734 * @typ: property's type or %G_TYPE_NONE. (Exact type, not is-a.)
735 * @pres: (out) (optional): location to store property value.
737 * Returns: %TRUE if @obj has a readable property named @property
738 * of type @typ.
740 gboolean
741 gnm_object_has_readable_prop (gconstpointer obj, const char *property,
742 GType typ, gpointer pres)
744 GObjectClass *klass;
745 GParamSpec *spec;
747 if (!obj)
748 return FALSE;
750 klass = G_OBJECT_GET_CLASS (G_OBJECT (obj));
751 spec = g_object_class_find_property (klass, property);
752 if (!spec ||
753 !(G_PARAM_READABLE & spec->flags) ||
754 (typ != G_TYPE_NONE && spec->value_type != typ))
755 return FALSE;
757 if (pres)
758 g_object_get (G_OBJECT (obj), property, pres, NULL);
759 return TRUE;
762 /* ------------------------------------------------------------------------- */
764 gint
765 gnm_float_equal (gnm_float const *a, const gnm_float *b)
767 return (*a == *b);
770 guint
771 gnm_float_hash (gnm_float const *d)
773 int expt;
774 gnm_float mant = gnm_frexp (gnm_abs (*d), &expt);
775 guint h = ((guint)(0x80000000u * mant)) ^ expt;
776 if (*d >= 0)
777 h ^= 0x55555555;
778 return h;
781 /* ------------------------------------------------------------------------- */
783 struct cb_compare {
784 GnmHashTableOrder order;
785 gpointer user;
788 static gint
789 cb_compare (gconstpointer a_, gconstpointer b_, gpointer user_data)
791 struct cb_compare *user = user_data;
792 gpointer *a = (gpointer )a_;
793 gpointer *b = (gpointer )b_;
795 return user->order (a[0], a[1], b[0], b[1], user->user);
800 * gnm_hash_table_foreach_ordered:
801 * @h: Hash table
802 * @callback: (scope call): #GHFunc
803 * @order: (scope call): Ordering function
804 * @user: user data for callback and order
806 * Like g_hash_table_foreach, but with an ordering imposed.
808 void
809 gnm_hash_table_foreach_ordered (GHashTable *h,
810 GHFunc callback,
811 GnmHashTableOrder order,
812 gpointer user)
814 unsigned ui;
815 GPtrArray *data;
816 struct cb_compare u;
817 GHashTableIter hiter;
818 gpointer key, value;
820 /* Gather all key-value pairs */
821 data = g_ptr_array_new ();
822 g_hash_table_iter_init (&hiter, h);
823 while (g_hash_table_iter_next (&hiter, &key, &value)) {
824 g_ptr_array_add (data, key);
825 g_ptr_array_add (data, value);
828 /* Sort according to given ordering */
829 u.order = order;
830 u.user = user;
831 g_qsort_with_data (data->pdata,
832 data->len / 2, 2 * sizeof (gpointer),
833 cb_compare,
834 &u);
836 /* Call user callback with all pairs */
837 for (ui = 0; ui < data->len; ui += 2)
838 callback (g_ptr_array_index (data, ui),
839 g_ptr_array_index (data, ui + 1),
840 user);
842 /* Clean up */
843 g_ptr_array_free (data, TRUE);
846 /* ------------------------------------------------------------------------- */
848 void
849 gnm_xml_in_doc_dispose_on_exit (GsfXMLInDoc **pdoc)
851 gutils_xml_in_docs = g_slist_prepend (gutils_xml_in_docs, pdoc);
854 /* ------------------------------------------------------------------------- */
857 * gnm_file_saver_get_sheet:
858 * @fs: #GOFileSaver
859 * @wbv: #WorkbookView
861 * For a single-sheet saver, this function determines what sheet to save.
863 * Returns: (transfer none): the sheet to export
865 Sheet *
866 gnm_file_saver_get_sheet (GOFileSaver const *fs, WorkbookView const *wbv)
868 Workbook *wb;
869 GPtrArray *sel;
871 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
872 g_return_val_if_fail (go_file_saver_get_save_scope (fs) ==
873 GO_FILE_SAVE_SHEET, NULL);
874 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
876 wb = wb_view_get_workbook (wbv);
878 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
879 if (sel) {
880 if (sel->len == 1)
881 return g_ptr_array_index (sel, 0);
882 g_critical ("Someone messed up sheet selection");
885 return wb_view_cur_sheet (wbv);
889 * gnm_file_saver_get_sheets:
890 * @fs: #GOFileSaver
891 * @wbv: #WorkbookView
892 * @default_all: If %TRUE, all sheets will be selected by default; if %FALSE,
893 * this function will return %NULL if no sheets were explicitly selected.
895 * For a workbook-scope saver, this function determines what sheets to save.
897 * Returns: (transfer container) (element-type Sheet): the sheets to export
899 * Note: the return value should be unreffed, not freed.
901 GPtrArray *
902 gnm_file_saver_get_sheets (GOFileSaver const *fs,
903 WorkbookView const *wbv,
904 gboolean default_all)
906 Workbook *wb;
907 GPtrArray *sel, *sheets;
909 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
910 g_return_val_if_fail (go_file_saver_get_save_scope (fs) ==
911 GO_FILE_SAVE_WORKBOOK, NULL);
912 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
914 wb = wb_view_get_workbook (wbv);
915 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
916 sheets = g_object_get_data (G_OBJECT (wb), SSCONVERT_SHEET_SET_KEY);
917 if (sel)
918 g_ptr_array_ref (sel);
919 else if (sheets)
920 sel = g_ptr_array_ref (sheets);
921 else if (default_all) {
922 int i;
923 sel = g_ptr_array_new ();
924 for (i = 0; i < workbook_sheet_count (wb); i++) {
925 Sheet *sheet = workbook_sheet_by_index (wb, i);
926 g_ptr_array_add (sel, sheet);
930 return sel;
933 gboolean
934 gnm_file_saver_common_export_option (GOFileSaver const *fs,
935 Workbook const *wb,
936 const char *key, const char *value,
937 GError **err)
939 if (err)
940 *err = NULL;
942 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), FALSE);
943 g_return_val_if_fail (GNM_IS_WORKBOOK (wb), FALSE);
944 g_return_val_if_fail (key != NULL, FALSE);
945 g_return_val_if_fail (value != NULL, FALSE);
947 if (strcmp (key, "sheet") == 0) {
948 GPtrArray *sheets;
949 Sheet *sheet = workbook_sheet_by_name (wb, value);
951 if (!sheet) {
952 if (err)
953 *err = g_error_new (go_error_invalid (), 0,
954 _("Unknown sheet \"%s\""),
955 value);
956 return TRUE;
959 sheets = g_object_get_data (G_OBJECT (wb), SSCONVERT_SHEET_SET_KEY);
960 if (!sheets) {
961 sheets = g_ptr_array_new ();
962 g_object_set_data_full (G_OBJECT (wb),
963 SSCONVERT_SHEET_SET_KEY,
964 sheets,
965 (GDestroyNotify)g_ptr_array_unref);
967 g_ptr_array_add (sheets, sheet);
969 return FALSE;
972 if (err)
973 *err = g_error_new (go_error_invalid (), 0,
974 _("Invalid export option \"%s\" for format %s"),
975 key,
976 go_file_saver_get_id (fs));
978 return TRUE;