Export: move the single-sheet selection logic out of the savers
[gnumeric.git] / src / gutils.c
blobd9c02e49712d7c8d30c30b09340056c54ec70300
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"
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"
37 static char *gnumeric_lib_dir;
38 static char *gnumeric_data_dir;
39 static char *gnumeric_locale_dir;
40 static char *gnumeric_usr_dir;
41 static char *gnumeric_usr_dir_unversioned;
42 static char *gnumeric_extern_plugin_dir;
43 static GSList *gutils_xml_in_docs;
45 static gboolean
46 running_in_tree (void)
48 const char *argv0 = g_get_prgname ();
50 if (!argv0)
51 return FALSE;
53 /* Sometime we see, e.g., "lt-gnumeric" as basename. */
55 char *base = g_path_get_basename (argv0);
56 gboolean has_lt_prefix = (strncmp (base, "lt-", 3) == 0);
57 g_free (base);
58 if (has_lt_prefix)
59 return TRUE;
62 /* Look for ".libs" as final path element. */
64 const char *dotlibs = strstr (argv0, ".libs/");
65 if (dotlibs &&
66 (dotlibs == argv0 || G_IS_DIR_SEPARATOR (dotlibs[-1])) &&
67 strchr (dotlibs + 6, G_DIR_SEPARATOR) == NULL)
68 return TRUE;
71 return FALSE;
74 static gboolean gutils_inited = FALSE;
76 void
77 gutils_init (void)
79 char const *home_dir;
81 // This function will end up being called twice in normal operation:
82 // once from gnm_pre_parse_init and once from gnm_init. Introspection
83 // will not get the first.
84 if (gutils_inited)
85 return;
87 #ifdef G_OS_WIN32
88 gchar *dir = g_win32_get_package_installation_directory_of_module (NULL);
89 gnumeric_lib_dir = g_build_filename (dir, "lib",
90 "gnumeric", GNM_VERSION_FULL,
91 NULL);
92 gnumeric_data_dir = g_build_filename (dir, "share",
93 "gnumeric", GNM_VERSION_FULL,
94 NULL);
95 gnumeric_locale_dir = g_build_filename (dir, "share", "locale", NULL);
96 gnumeric_extern_plugin_dir = g_build_filename
97 (dir, "lib", "gnumeric", GNM_API_VERSION, "plugins",
98 NULL);
99 g_free (dir);
100 #else
101 if (running_in_tree ()) {
102 const char *argv0 = g_get_prgname ();
103 char *dotlibs = g_path_get_dirname (argv0);
104 char *top = g_build_filename (dotlibs, "..", "../", NULL);
105 char *plugins = g_build_filename (top, PLUGIN_SUBDIR, NULL);
106 if (g_file_test (plugins, G_FILE_TEST_IS_DIR))
107 gnumeric_lib_dir =
108 go_filename_simplify (top, GO_DOTDOT_SYNTACTIC,
109 FALSE);
110 g_free (top);
111 g_free (plugins);
112 g_free (dotlibs);
113 if (0) g_printerr ("Running in-tree\n");
116 if (!gnumeric_lib_dir)
117 gnumeric_lib_dir = g_strdup (GNUMERIC_LIBDIR);
118 gnumeric_data_dir = g_strdup (GNUMERIC_DATADIR);
119 gnumeric_locale_dir = g_strdup (GNUMERIC_LOCALEDIR);
120 gnumeric_extern_plugin_dir = g_strdup (GNUMERIC_EXTERNPLUGINDIR);
121 #endif
122 home_dir = g_get_home_dir ();
123 gnumeric_usr_dir_unversioned = home_dir
124 ? g_build_filename (home_dir, ".gnumeric", NULL)
125 : NULL;
126 gnumeric_usr_dir = gnumeric_usr_dir_unversioned
127 ? g_build_filename (gnumeric_usr_dir_unversioned, GNM_VERSION_FULL, NULL)
128 : NULL;
130 gutils_inited = TRUE;
133 void
134 gutils_shutdown (void)
136 GSList *l;
138 g_free (gnumeric_lib_dir);
139 gnumeric_lib_dir = NULL;
140 g_free (gnumeric_data_dir);
141 gnumeric_data_dir = NULL;
142 g_free (gnumeric_locale_dir);
143 gnumeric_locale_dir = NULL;
144 g_free (gnumeric_usr_dir);
145 gnumeric_usr_dir = NULL;
146 g_free (gnumeric_usr_dir_unversioned);
147 gnumeric_usr_dir_unversioned = NULL;
148 g_free (gnumeric_extern_plugin_dir);
149 gnumeric_extern_plugin_dir = NULL;
151 for (l = gutils_xml_in_docs; l; l = l->next) {
152 GsfXMLInDoc **pdoc = l->data;
153 gsf_xml_in_doc_free (*pdoc);
154 *pdoc = NULL;
156 g_slist_free (gutils_xml_in_docs);
157 gutils_xml_in_docs = NULL;
160 char const *
161 gnm_sys_lib_dir (void)
163 return gnumeric_lib_dir;
166 char const *
167 gnm_sys_data_dir (void)
169 return gnumeric_data_dir;
172 char const *
173 gnm_sys_extern_plugin_dir (void)
175 return gnumeric_extern_plugin_dir;
178 char const *
179 gnm_locale_dir (void)
181 return gnumeric_locale_dir;
184 char const *
185 gnm_usr_dir (gboolean versioned)
187 return versioned ? gnumeric_usr_dir : gnumeric_usr_dir_unversioned;
191 static gboolean
192 all_ascii (const char *s)
194 while ((guchar)*s < 0x7f) {
195 if (*s)
196 s++;
197 else
198 return TRUE;
200 return FALSE;
204 * Like strto[ld], but...
205 * 1. handles non-ascii characters
206 * 2. disallows 0x000.0p+00 and 0.0d+00
207 * 3. ensures sane errno on exit
209 gnm_float
210 gnm_utf8_strto (const char *s, char **end)
212 const char *p;
213 int sign;
214 char *dummy_end;
215 GString *ascii;
216 GString const *decimal = go_locale_get_decimal ();
217 gboolean seen_decimal = FALSE;
218 gboolean seen_digit = FALSE;
219 size_t spaces = 0;
220 gnm_float res;
221 int save_errno;
223 if (all_ascii (s)) {
224 res = gnm_strto (s, end);
225 goto handle_denormal;
228 ascii = g_string_sized_new (100);
230 if (!end)
231 end = &dummy_end;
233 p = s;
234 while (g_unichar_isspace (g_utf8_get_char (p))) {
235 p = g_utf8_next_char (p);
236 spaces++;
239 sign = go_unichar_issign (g_utf8_get_char (p));
240 if (sign) {
241 g_string_append_c (ascii, "-/+"[sign + 1]);
242 p = g_utf8_next_char (p);
245 do {
246 if (strncmp (p, decimal->str, decimal->len) == 0) {
247 if (seen_decimal)
248 break;
249 seen_decimal = TRUE;
250 go_string_append_gstring (ascii, decimal);
251 p += decimal->len;
252 } else if (g_unichar_isdigit (g_utf8_get_char (p))) {
253 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
254 p = g_utf8_next_char (p);
255 seen_digit = TRUE;
256 } else
257 break;
258 } while (1);
260 if (!seen_digit) {
261 /* No conversion, bail to gnm_strto for nan etc. */
262 g_string_free (ascii, TRUE);
263 return gnm_strto (s, end);
266 if (*p == 'e' || *p == 'E') {
267 int sign;
269 g_string_append_c (ascii, 'e');
270 p = g_utf8_next_char (p);
272 sign = go_unichar_issign (g_utf8_get_char (p));
273 if (sign) {
274 g_string_append_c (ascii, "-/+"[sign + 1]);
275 p = g_utf8_next_char (p);
277 while (g_unichar_isdigit (g_utf8_get_char (p))) {
278 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
279 p = g_utf8_next_char (p);
283 res = gnm_strto (ascii->str, end);
284 save_errno = errno;
285 *end = g_utf8_offset_to_pointer
286 (s, spaces + g_utf8_pointer_to_offset (ascii->str, *end));
287 g_string_free (ascii, TRUE);
289 errno = save_errno;
291 handle_denormal:
292 save_errno = errno;
293 if (res != 0 && gnm_abs (res) < GNM_MIN)
294 errno = 0;
295 else
296 errno = save_errno;
298 return res;
302 * Like strtol, but...
303 * 1. handles non-ascii characters
304 * 2. assumes base==10
305 * 3. ensures sane errno on exit
307 long
308 gnm_utf8_strtol (const char *s, char **end)
310 const char *p;
311 int sign;
312 char *dummy_end;
313 unsigned long res = 0, lim, limd;
315 if (!end)
316 end = &dummy_end;
318 p = s;
319 while (g_unichar_isspace (g_utf8_get_char (p)))
320 p = g_utf8_next_char (p);
322 sign = go_unichar_issign (g_utf8_get_char (p));
323 if (sign)
324 p = g_utf8_next_char (p);
325 if (sign < 0) {
326 lim = (-(unsigned long)LONG_MIN) / 10u;
327 limd = (-(unsigned long)LONG_MIN) % 10u;
328 } else {
329 lim = (unsigned long)LONG_MAX / 10u;
330 limd = (unsigned long)LONG_MAX % 10u;
333 if (!g_unichar_isdigit (g_utf8_get_char (p))) {
334 errno = 0;
335 *end = (char *)s;
336 return 0;
339 while (g_unichar_isdigit (g_utf8_get_char (p))) {
340 guint8 dig = g_unichar_digit_value (g_utf8_get_char (p));
341 p = g_utf8_next_char (p);
343 if (res > lim || (res == lim && dig > limd)) {
344 /* Overflow */
345 while (g_unichar_isdigit (g_utf8_get_char (p)))
346 p = g_utf8_next_char (p);
347 *end = (char *)p;
348 errno = ERANGE;
349 return sign < 0 ? LONG_MIN : LONG_MAX;
352 res = res * 10u + dig;
354 *end = (char *)p;
355 errno = 0;
356 return sign < 0 ? (long)-res : (long)res;
361 gnm_regcomp_XL (GORegexp *preg, char const *pattern, int cflags,
362 gboolean anchor_start, gboolean anchor_end)
364 GString *res = g_string_new (NULL);
365 int retval;
367 if (anchor_start)
368 g_string_append_c (res, '^');
370 while (*pattern) {
371 switch (*pattern) {
372 case '*':
373 g_string_append (res, ".*");
374 pattern++;
375 break;
377 case '?':
378 g_string_append_c (res, '.');
379 pattern++;
380 break;
382 case '~':
383 if (pattern[1] == '*' ||
384 pattern[1] == '?' ||
385 pattern[1] == '~')
386 pattern++;
387 /* Fall through */
388 default:
389 pattern = go_regexp_quote1 (res, pattern);
393 if (anchor_end)
394 g_string_append_c (res, '$');
396 retval = go_regcomp (preg, res->str, cflags);
397 g_string_free (res, TRUE);
398 return retval;
402 * gnm_excel_search_impl:
403 * @needle: the pattern to search for, see gnm_regcomp_XL.
404 * @haystack: the string to search in.
405 * @skip: zero-based search start point in characters.
407 * Returns: -1 for a non-match, or zero-based location in
408 * characters.
410 * The is the implementation of Excel's SEARCH function.
411 * However, note that @skip and return value are zero-based.
414 gnm_excel_search_impl (const char *needle, const char *haystack,
415 size_t skip)
417 const char *hay2;
418 size_t i;
419 GORegexp r;
421 for (i = skip, hay2 = haystack; i > 0; i--) {
422 if (*hay2 == 0)
423 return -1;
424 hay2 = g_utf8_next_char (hay2);
427 if (gnm_regcomp_XL (&r, needle, GO_REG_ICASE, FALSE, FALSE) == GO_REG_OK) {
428 GORegmatch rm;
430 switch (go_regexec (&r, hay2, 1, &rm, 0)) {
431 case GO_REG_NOMATCH:
432 break;
433 case GO_REG_OK:
434 go_regfree (&r);
435 return skip +
436 g_utf8_pointer_to_offset (hay2, hay2 + rm.rm_so);
437 default:
438 g_warning ("Unexpected go_regexec result");
440 go_regfree (&r);
441 } else {
442 g_warning ("Unexpected regcomp result");
445 return -1;
449 #if 0
450 static char const *
451 color_to_string (PangoColor color)
453 static char result[100];
454 sprintf (result, "%04x:%04x:%04x", color.red, color.green, color.blue);
455 return result;
458 static const char *
459 enum_name (GType typ, int i)
461 static char result[100];
462 GEnumClass *ec = g_type_class_ref (typ);
464 if (ec) {
465 GEnumValue *ev = g_enum_get_value (ec, i);
466 g_type_class_unref (ec);
468 if (ev && ev->value_nick)
469 return ev->value_nick;
470 if (ev && ev->value_name)
471 return ev->value_name;
474 sprintf (result, "%d", i);
475 return result;
478 static gboolean
479 cb_gnm_pango_attr_dump (PangoAttribute *attr, gpointer user_data)
481 g_print (" start=%u; end=%u\n", attr->start_index, attr->end_index);
482 switch (attr->klass->type) {
483 case PANGO_ATTR_FAMILY:
484 g_print (" family=\"%s\"\n", ((PangoAttrString *)attr)->value);
485 break;
486 case PANGO_ATTR_LANGUAGE:
487 g_print (" language=\"%s\"\n", pango_language_to_string (((PangoAttrLanguage *)attr)->value));
488 break;
489 case PANGO_ATTR_STYLE:
490 g_print (" style=%s\n",
491 enum_name (PANGO_TYPE_STYLE, ((PangoAttrInt *)attr)->value));
492 break;
493 case PANGO_ATTR_WEIGHT:
494 g_print (" weight=%s\n",
495 enum_name (PANGO_TYPE_WEIGHT, ((PangoAttrInt *)attr)->value));
496 break;
497 case PANGO_ATTR_VARIANT:
498 g_print (" variant=%s\n",
499 enum_name (PANGO_TYPE_VARIANT, ((PangoAttrInt *)attr)->value));
500 break;
501 case PANGO_ATTR_STRETCH:
502 g_print (" stretch=%s\n",
503 enum_name (PANGO_TYPE_STRETCH, ((PangoAttrInt *)attr)->value));
504 break;
505 case PANGO_ATTR_UNDERLINE:
506 g_print (" underline=%s\n",
507 enum_name (PANGO_TYPE_UNDERLINE, ((PangoAttrInt *)attr)->value));
508 break;
509 case PANGO_ATTR_STRIKETHROUGH:
510 g_print (" strikethrough=%d\n", ((PangoAttrInt *)attr)->value);
511 break;
512 case PANGO_ATTR_RISE:
513 g_print (" rise=%d\n", ((PangoAttrInt *)attr)->value);
514 break;
515 case PANGO_ATTR_FALLBACK:
516 g_print (" fallback=%d\n", ((PangoAttrInt *)attr)->value);
517 break;
518 case PANGO_ATTR_LETTER_SPACING:
519 g_print (" letter_spacing=%d\n", ((PangoAttrInt *)attr)->value);
520 break;
521 case PANGO_ATTR_SIZE:
522 g_print (" size=%d%s\n",
523 ((PangoAttrSize *)attr)->size,
524 ((PangoAttrSize *)attr)->absolute ? " abs" : "");
525 break;
526 case PANGO_ATTR_SCALE:
527 g_print (" scale=%g\n", ((PangoAttrFloat *)attr)->value);
528 break;
529 case PANGO_ATTR_FOREGROUND:
530 g_print (" foreground=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
531 break;
532 case PANGO_ATTR_BACKGROUND:
533 g_print (" background=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
534 break;
535 case PANGO_ATTR_UNDERLINE_COLOR:
536 g_print (" underline_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
537 break;
538 case PANGO_ATTR_STRIKETHROUGH_COLOR:
539 g_print (" strikethrough_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
540 break;
541 case PANGO_ATTR_FONT_DESC: {
542 char *desc = pango_font_description_to_string (((PangoAttrFontDesc*)attr)->desc);
543 g_print (" font=\"%s\"\n", desc);
544 g_free (desc);
545 break;
547 default:
548 g_print (" type=%s\n", enum_name (PANGO_TYPE_ATTR_TYPE, attr->klass->type));
551 return FALSE;
554 void
555 gnm_pango_attr_dump (PangoAttrList *list)
557 g_print ("PangoAttrList at %p\n", list);
558 pango_attr_list_filter (list, cb_gnm_pango_attr_dump, NULL);
560 #endif
563 static gboolean
564 cb_gnm_pango_attr_list_equal (PangoAttribute *a, gpointer _sl)
566 GSList **sl = _sl;
567 *sl = g_slist_prepend (*sl, a);
568 return FALSE;
572 * This is a bit of a hack. It might claim a difference even when things
573 * actually are equal. But not the other way around.
575 gboolean
576 gnm_pango_attr_list_equal (PangoAttrList const *l1, PangoAttrList const *l2)
578 if (l1 == l2)
579 return TRUE;
580 else if (l1 == NULL || l2 == NULL)
581 return FALSE;
582 else {
583 gboolean res;
584 GSList *sl1 = NULL, *sl2 = NULL;
585 (void)pango_attr_list_filter ((PangoAttrList *)l1,
586 cb_gnm_pango_attr_list_equal,
587 &sl1);
588 (void)pango_attr_list_filter ((PangoAttrList *)l2,
589 cb_gnm_pango_attr_list_equal,
590 &sl2);
592 while (sl1 && sl2) {
593 const PangoAttribute *a1 = sl1->data;
594 const PangoAttribute *a2 = sl2->data;
595 if (a1->start_index != a2->start_index ||
596 a1->end_index != a2->end_index ||
597 !pango_attribute_equal (a1, a2))
598 break;
599 sl1 = g_slist_delete_link (sl1, sl1);
600 sl2 = g_slist_delete_link (sl2, sl2);
603 res = (sl1 == sl2);
604 g_slist_free (sl1);
605 g_slist_free (sl2);
606 return res;
610 /* ------------------------------------------------------------------------- */
612 struct _GnmLocale {
613 char *num_locale;
614 char *monetary_locale;
617 * gnm_push_C_locale: (skip)
619 * Returns the current locale, and sets the locale and the value-format
620 * engine's locale to 'C'. The caller must call gnm_pop_C_locale to free the
621 * result and restore the previous locale.
623 GnmLocale *
624 gnm_push_C_locale (void)
626 GnmLocale *old = g_new0 (GnmLocale, 1);
628 old->num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
629 go_setlocale (LC_NUMERIC, "C");
630 old->monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
631 go_setlocale (LC_MONETARY, "C");
632 go_locale_untranslated_booleans ();
634 return old;
638 * gnm_pop_C_locale: (skip)
639 * @locale: #GnmLocale
641 * Frees the result of gnm_push_C_locale and restores the original locale.
643 void
644 gnm_pop_C_locale (GnmLocale *locale)
646 /* go_setlocale restores bools to locale translation */
647 go_setlocale (LC_MONETARY, locale->monetary_locale);
648 g_free (locale->monetary_locale);
649 go_setlocale (LC_NUMERIC, locale->num_locale);
650 g_free (locale->num_locale);
651 g_free (locale);
654 /* ------------------------------------------------------------------------- */
656 gboolean
657 gnm_debug_flag (const char *flag)
659 GDebugKey key;
660 key.key = (char *)flag;
661 key.value = 1;
663 return g_parse_debug_string (g_getenv ("GNM_DEBUG"), &key, 1) != 0;
666 /* ------------------------------------------------------------------------- */
668 void
669 gnm_string_add_number (GString *buf, gnm_float d)
671 size_t old_len = buf->len;
672 double d2;
673 static int digits;
675 if (digits == 0) {
676 gnm_float l10 = gnm_log10 (FLT_RADIX);
677 digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
678 (l10 == (int)l10 ? 0 : 1);
681 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits - 1, d);
682 d2 = gnm_strto (buf->str + old_len, NULL);
684 if (d != d2) {
685 g_string_truncate (buf, old_len);
686 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits, d);
690 /* ------------------------------------------------------------------------- */
692 void
693 gnm_insert_meta_date (GODoc *doc, char const *name)
695 GValue *value = g_new0 (GValue, 1);
696 GTimeVal tm;
697 GsfTimestamp *ts = gsf_timestamp_new ();
699 g_get_current_time (&tm);
700 tm.tv_usec = 0L;
702 gsf_timestamp_set_time (ts, tm.tv_sec);
703 g_value_init (value, GSF_TIMESTAMP_TYPE);
704 gsf_timestamp_to_value (ts, value);
705 gsf_timestamp_free (ts);
707 gsf_doc_meta_data_insert (go_doc_get_meta_data (doc),
708 g_strdup (name),
709 value);
712 /* ------------------------------------------------------------------------- */
715 * gnm_object_get_bool:
716 * @o: #GObject
717 * @name: property name
719 * Returns: the value of @o's boolean property @name.
721 gboolean
722 gnm_object_get_bool (gpointer o, const char *name)
724 gboolean b;
725 g_object_get (o, name, &b, NULL);
726 return b;
730 * gnm_object_has_readable_prop:
731 * @obj: #GObject
732 * @property: property name
733 * @typ: property's type or %G_TYPE_NONE. (Exact type, not is-a.)
734 * @pres: (out) (optional): location to store property value.
736 * Returns: %TRUE if @obj has a readable property named @property
737 * of type @typ.
739 gboolean
740 gnm_object_has_readable_prop (gconstpointer obj, const char *property,
741 GType typ, gpointer pres)
743 GObjectClass *klass;
744 GParamSpec *spec;
746 if (!obj)
747 return FALSE;
749 klass = G_OBJECT_GET_CLASS (G_OBJECT (obj));
750 spec = g_object_class_find_property (klass, property);
751 if (!spec ||
752 !(G_PARAM_READABLE & spec->flags) ||
753 (typ != G_TYPE_NONE && spec->value_type != typ))
754 return FALSE;
756 if (pres)
757 g_object_get (G_OBJECT (obj), property, pres, NULL);
758 return TRUE;
761 /* ------------------------------------------------------------------------- */
763 gint
764 gnm_float_equal (gnm_float const *a, const gnm_float *b)
766 return (*a == *b);
769 guint
770 gnm_float_hash (gnm_float const *d)
772 int expt;
773 gnm_float mant = gnm_frexp (gnm_abs (*d), &expt);
774 guint h = ((guint)(0x80000000u * mant)) ^ expt;
775 if (*d >= 0)
776 h ^= 0x55555555;
777 return h;
780 /* ------------------------------------------------------------------------- */
782 struct cb_compare {
783 GnmHashTableOrder order;
784 gpointer user;
787 static gint
788 cb_compare (gconstpointer a_, gconstpointer b_, gpointer user_data)
790 struct cb_compare *user = user_data;
791 gpointer *a = (gpointer )a_;
792 gpointer *b = (gpointer )b_;
794 return user->order (a[0], a[1], b[0], b[1], user->user);
799 * gnm_hash_table_foreach_ordered:
800 * @h: Hash table
801 * @callback: (scope async): #GHFunc
802 * @order: (scope async): Ordering function
803 * @user: user data for callback and order
805 * Like g_hash_table_foreach, but with an ordering imposed.
807 void
808 gnm_hash_table_foreach_ordered (GHashTable *h,
809 GHFunc callback,
810 GnmHashTableOrder order,
811 gpointer user)
813 unsigned ui;
814 GPtrArray *data;
815 struct cb_compare u;
816 GHashTableIter hiter;
817 gpointer key, value;
819 /* Gather all key-value pairs */
820 data = g_ptr_array_new ();
821 g_hash_table_iter_init (&hiter, h);
822 while (g_hash_table_iter_next (&hiter, &key, &value)) {
823 g_ptr_array_add (data, key);
824 g_ptr_array_add (data, value);
827 /* Sort according to given ordering */
828 u.order = order;
829 u.user = user;
830 g_qsort_with_data (data->pdata,
831 data->len / 2, 2 * sizeof (gpointer),
832 cb_compare,
833 &u);
835 /* Call user callback with all pairs */
836 for (ui = 0; ui < data->len; ui += 2)
837 callback (g_ptr_array_index (data, ui),
838 g_ptr_array_index (data, ui + 1),
839 user);
841 /* Clean up */
842 g_ptr_array_free (data, TRUE);
845 /* ------------------------------------------------------------------------- */
847 void
848 gnm_xml_in_doc_dispose_on_exit (GsfXMLInDoc **pdoc)
850 gutils_xml_in_docs = g_slist_prepend (gutils_xml_in_docs, pdoc);
853 /* ------------------------------------------------------------------------- */
856 * gnm_file_saver_get_sheet:
857 * @fs: #GOFileSaver
858 * @wbv: #WorkbookView
860 * For a single-sheet saver, this function determines what sheet to save.
862 * Returns: (transfer none): the sheet to export
864 Sheet *
865 gnm_file_saver_get_sheet (GOFileSaver const *fs, WorkbookView const *wbv)
867 Workbook *wb;
868 GPtrArray *sel;
870 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
871 g_return_val_if_fail (go_file_saver_get_save_scope (fs) == GO_FILE_SAVE_SHEET, NULL);
872 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
874 wb = wb_view_get_workbook (wbv);
876 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
877 if (sel) {
878 if (sel->len)
879 return g_ptr_array_index (sel, 0);
880 g_critical ("Someone messed up sheet selection");
883 return wb_view_cur_sheet (wbv);