+static void
+get_path (const struct spv_item *item, struct item_path *path)
+{
+ size_t allocated = 10;
+ path->nodes = path->stub;
+ path->n = 0;
+
+ while (item)
+ {
+ if (path->n >= allocated)
+ {
+ if (path->nodes == path->stub)
+ path->nodes = xmemdup (path->stub, sizeof path->stub);
+ path->nodes = x2nrealloc (path->nodes, &allocated,
+ sizeof *path->nodes);
+ }
+ path->nodes[path->n++] = item;
+ item = item->parent;
+ }
+
+ for (size_t i = 0; i < path->n / 2; i++)
+ swap_nodes (&path->nodes[i], &path->nodes[path->n - i - 1]);
+}
+
+static void
+free_path (struct item_path *path)
+{
+ if (path && path->nodes != path->stub)
+ free (path->nodes);
+}
+
+static void
+dump_heading_transition (const struct spv_item *old,
+ const struct spv_item *new)
+{
+ if (old == new)
+ return;
+
+ struct item_path old_path, new_path;
+ get_path (old, &old_path);
+ get_path (new, &new_path);
+
+ size_t common = 0;
+ for (; common < old_path.n && common < new_path.n; common++)
+ if (old_path.nodes[common] != new_path.nodes[common])
+ break;
+
+ for (size_t i = common; i < old_path.n; i++)
+ group_close_item_submit (group_close_item_create ());
+ for (size_t i = common; i < new_path.n; i++)
+ group_open_item_submit (group_open_item_create (
+ new_path.nodes[i]->command_id));
+
+ free_path (&old_path);
+ free_path (&new_path);
+}
+
+void
+read_spv_file (const char *filename)
+{
+ struct spv_reader *spv;
+ char *error = spv_open (filename, &spv);
+ if (error)
+ {
+ /* XXX */
+ fprintf (stderr, "%s\n", error);
+ return;
+ }
+
+ struct spv_item **items;
+ size_t n_items;
+ spv_select (spv, NULL, 0, &items, &n_items);
+ struct spv_item *prev_heading = spv_get_root (spv);
+ for (size_t i = 0; i < n_items; i++)
+ {
+ struct spv_item *heading
+ = items[i]->type == SPV_ITEM_HEADING ? items[i] : items[i]->parent;
+ dump_heading_transition (prev_heading, heading);
+ if (items[i]->type == SPV_ITEM_TEXT)
+ spv_text_submit (items[i]);
+ else if (items[i]->type == SPV_ITEM_TABLE)
+ pivot_table_submit (spv_item_get_table (items[i]));
+ prev_heading = heading;
+ }
+ dump_heading_transition (prev_heading, spv_get_root (spv));
+ free (items);
+ spv_close (spv);
+}
+
+/* Callback for the file_open action.
+ Prompts for a filename and opens it */
+void
+psppire_window_open (PsppireWindow *de)
+{
+ GtkWidget *dialog = psppire_window_file_chooser_dialog (de);
+
+ gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog), relocate (examples_dir), NULL);
+
+ switch (gtk_dialog_run (GTK_DIALOG (dialog)))
+ {
+ case GTK_RESPONSE_ACCEPT:
+ {
+ gchar *name =
+ gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ const gchar **cs = NULL;
+ g_get_filename_charsets (&cs);
+
+ gchar *encoding = psppire_encoding_selector_get_encoding (
+ gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
+
+ struct file_handle *fh = fh_create_file (NULL, name, cs[0], fh_default_properties ());
+
+ int retval = any_reader_detect (fh, NULL);
+ if (retval == 1)
+ open_data_window (de, name, encoding, NULL);
+ else if (retval == 0)
+ {
+ char *error = spv_detect (name);
+ if (!error)
+ read_spv_file (name);
+ else
+ {
+ free (error);
+ open_syntax_window (name, encoding);
+ }
+ }
+
+ g_free (encoding);
+ fh_unref (fh);
+ g_free (name);
+ }
+ break;
+ default:
+ break;
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+
+/* Puts FILE_NAME (encoded in the glib file name encoding) into the recent list
+ with associated MIME_TYPE. If it's already in the list, it moves it to the
+ top. */
+void
+add_most_recent (const char *file_name,
+ const char *mime_type, const char *encoding)
+{
+ gchar *uri = g_filename_to_uri (file_name, NULL, NULL);
+ if (uri)
+ {
+ GtkRecentData recent_data;
+ gchar *full_mime_type;
+
+ if (encoding && encoding[0])
+ full_mime_type = g_strdup_printf ("%s; charset=%s",
+ mime_type, encoding);
+ else
+ full_mime_type = g_strdup (mime_type);
+
+ recent_data.display_name = NULL;
+ recent_data.description = NULL;
+ recent_data.mime_type = full_mime_type;
+ recent_data.app_name = CONST_CAST (gchar *, g_get_application_name ());
+ recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
+ recent_data.groups = NULL;
+ recent_data.is_private = FALSE;
+
+ gtk_recent_manager_add_full (gtk_recent_manager_get_default (),
+ uri, &recent_data);
+
+ g_free (recent_data.app_exec);
+ g_free (full_mime_type);
+ }