From c473f9ca75fc61a68e9e7e3bd7cb5f36dd0f7cf5 Mon Sep 17 00:00:00 2001 From: John Darrington Date: Thu, 6 Feb 2014 21:50:11 +0100 Subject: [PATCH] Means Dialog: Fix issue where the wrong syntax was generated. Addresses Bug #41433 --- NEWS | 2 + src/ui/gui/dict-display.c | 48 +++++-- src/ui/gui/dict-display.h | 11 +- src/ui/gui/means.ui | 158 ++++++++++++++--------- src/ui/gui/psppire-dialog-action-means.c | 7 +- src/ui/gui/psppire-means-layer.c | 108 +++++++++------- src/ui/gui/psppire-means-layer.h | 5 +- src/ui/gui/psppire-selector.c | 81 +++++++++--- src/ui/gui/psppire.c | 3 + 9 files changed, 286 insertions(+), 137 deletions(-) diff --git a/NEWS b/NEWS index 51dc42cd1c..6949019e58 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ Changes since 0.8.2: - File|Display Data File Information|External File... now allows an encoding to be selected. + - A problem with the Means dialog has been resolved (bug #41433). + * System file related improvements: - With ENCODING="DETECT", SYSFILE INFO can now help the user to diff --git a/src/ui/gui/dict-display.c b/src/ui/gui/dict-display.c index 77da88bd98..cb46ad5716 100644 --- a/src/ui/gui/dict-display.c +++ b/src/ui/gui/dict-display.c @@ -24,6 +24,7 @@ #include "psppire-dict.h" #include "psppire-dictview.h" +#include "psppire-means-layer.h" #include "psppire-var-ptr.h" #include "psppire-var-view.h" #include "psppire-select-dest.h" @@ -87,23 +88,22 @@ insert_source_row_into_entry (GtkTreeIter iter, } -void -insert_source_row_into_tree_view (GtkTreeIter iter, - GtkWidget *dest, - GtkTreeModel *model, - gpointer data - ) + +static void +insert_source_row_into_tree_model (GtkTreeIter source_iter, + GtkTreeModel *dest_model, + GtkTreeModel *source_model, + gpointer data) { GtkTreePath *path; GtkTreeIter dest_iter; GtkTreeIter dict_iter; gint *row ; - GtkTreeModel *destmodel = gtk_tree_view_get_model (GTK_TREE_VIEW (dest)); const struct variable *var; GtkTreeModel *dict; - get_base_model (model, &iter, &dict, &dict_iter); + get_base_model (source_model, &source_iter, &dict, &dict_iter); path = gtk_tree_model_get_path (dict, &dict_iter); @@ -111,15 +111,43 @@ insert_source_row_into_tree_view (GtkTreeIter iter, var = psppire_dict_get_variable (PSPPIRE_DICT (dict), *row); - gtk_list_store_append (GTK_LIST_STORE (destmodel), &dest_iter); + gtk_list_store_append (GTK_LIST_STORE (dest_model), &dest_iter); - gtk_list_store_set (GTK_LIST_STORE (destmodel), &dest_iter, 0, var, -1); + gtk_list_store_set (GTK_LIST_STORE (dest_model), &dest_iter, 0, var, -1); gtk_tree_path_free (path); } +void +insert_source_row_into_tree_view (GtkTreeIter iter, + GtkWidget *dest, + GtkTreeModel *model, + gpointer data) +{ + GtkTreeModel *destmodel = gtk_tree_view_get_model (GTK_TREE_VIEW (dest)); + + insert_source_row_into_tree_model (iter, destmodel, model, data); +} + + +void +insert_source_row_into_layers (GtkTreeIter iter, + GtkWidget *dest, + GtkTreeModel *model, + gpointer data) +{ + GtkTreeModel *destmodel = psppire_means_layer_get_model (PSPPIRE_MEANS_LAYER (dest)); + + insert_source_row_into_tree_model (iter, destmodel, model, data); + + psppire_means_layer_update (PSPPIRE_MEANS_LAYER (dest)); +} + + + + gboolean is_currently_in_entry (GtkTreeModel *model, GtkTreeIter *iter, PsppireSelector *selector) diff --git a/src/ui/gui/dict-display.h b/src/ui/gui/dict-display.h index b3b395bb81..6a086b7ada 100644 --- a/src/ui/gui/dict-display.h +++ b/src/ui/gui/dict-display.h @@ -31,8 +31,15 @@ void insert_source_row_into_tree_view (GtkTreeIter source_iter, GtkWidget *dest, GtkTreeModel *source_model, - gpointer data - ); + gpointer data); + + +/* A SelectItemsFunc function for PsppireMeansLayers widgets */ +void insert_source_row_into_layers (GtkTreeIter source_iter, + GtkWidget *dest, + GtkTreeModel *source_model, + gpointer data); + /* A SelectItemsFunc function for GtkEntry widgets */ void insert_source_row_into_entry (GtkTreeIter source_iter, diff --git a/src/ui/gui/means.ui b/src/ui/gui/means.ui index f0c2138f2c..a650a6bba9 100644 --- a/src/ui/gui/means.ui +++ b/src/ui/gui/means.ui @@ -1,7 +1,7 @@ - + - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -44,74 +44,82 @@ 0 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0.25 - 0 - 0 - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - all-variables - stat-variables - - - - - False - False - 1 - - True True vertical - + True - 0 - none - + True - 12 + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0.25 + 0 + 0 - + True True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - never - automatic - etched-in + 5 + all-variables + stat-variables + + + + + False + False + 0 + + + + + True + 0 + none + + + True + 12 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - False + never + automatic + etched-in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + False + + + + + True + _Dependent List: + True + True + stat-variables + + - - - - True - _Dependent List: - True - True - stat-variables - + + 1 + @@ -120,25 +128,57 @@ - + True - 0 - none - + True - 12 + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0.25 + 0 + 0 - + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + all-variables + + + False + False + 0 + - - + + True - _Independent List: - True + 0 + none + + + True + 12 + + + + + + + + True + _Independent List: + True + + + + 1 + @@ -148,7 +188,7 @@ - 2 + 1 diff --git a/src/ui/gui/psppire-dialog-action-means.c b/src/ui/gui/psppire-dialog-action-means.c index 81e9bb9052..807d3fc38b 100644 --- a/src/ui/gui/psppire-dialog-action-means.c +++ b/src/ui/gui/psppire-dialog-action-means.c @@ -100,6 +100,8 @@ psppire_dialog_action_means_activate (GtkAction *a) GtkBuilder *xml = builder_new ("means.ui"); GtkWidget *vb = get_widget_assert (xml, "alignment3"); + GtkWidget *selector = get_widget_assert (xml, "layer-selector"); + act->layer = psppire_means_layer_new (); gtk_container_add (GTK_CONTAINER (vb), act->layer); gtk_widget_show (act->layer); @@ -112,7 +114,10 @@ psppire_dialog_action_means_activate (GtkAction *a) "predicate", var_is_numeric, NULL); - psppire_means_layer_set_source (PSPPIRE_MEANS_LAYER (act->layer), pda->source); + g_object_set (selector, + "dest-widget", act->layer, + NULL); + psppire_dialog_action_set_valid_predicate (pda, (void *) dialog_state_valid); psppire_dialog_action_set_refresh (pda, dialog_refresh); diff --git a/src/ui/gui/psppire-means-layer.c b/src/ui/gui/psppire-means-layer.c index a9b1139f18..8046b00765 100644 --- a/src/ui/gui/psppire-means-layer.c +++ b/src/ui/gui/psppire-means-layer.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2012 Free Software Foundation + Copyright (C) 2012, 2014 Free Software Foundation This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,6 @@ #include #include "psppire-means-layer.h" -#include "psppire-selector.h" #include "psppire-var-view.h" #include @@ -62,8 +61,31 @@ psppire_means_layer_class_init (PsppireMeansLayerClass *class) object_class->dispose = psppire_means_layer_dispose; } + +static void +refresh_view (PsppireMeansLayer *ml) +{ + GtkTreeModel *tm; + g_return_if_fail (ml->current_layer >= 0); + tm = g_ptr_array_index (ml->layer, ml->current_layer); + gtk_tree_view_set_model (GTK_TREE_VIEW (ml->var_view), tm); +} + + static void -update (PsppireMeansLayer *ml) +add_new_layer (PsppireMeansLayer *ml) +{ + /* Add a model and take a reference to it */ + GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ml->var_view)); + g_ptr_array_add (ml->layer, tm); + g_signal_connect_swapped (tm, "row-inserted", G_CALLBACK (refresh_view), ml); + + g_object_ref (tm); +} + + +void +psppire_means_layer_update (PsppireMeansLayer *ml) { gchar *l; @@ -77,29 +99,34 @@ update (PsppireMeansLayer *ml) g_free (l); gtk_widget_set_sensitive (ml->back, ml->current_layer > 0); - gtk_widget_set_sensitive (ml->forward, - psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (ml->var_view), - NULL)); + + { + GtkTreeIter dummy; + GtkTreeModel *tm = g_ptr_array_index (ml->layer, ml->current_layer); + + g_return_if_fail (GTK_IS_TREE_MODEL (tm)); + + gtk_widget_set_sensitive (ml->forward, + gtk_tree_model_get_iter_first (tm, &dummy)); + } } static void on_forward (PsppireMeansLayer *ml) { - ml->current_layer++; - if (ml->current_layer >= ml->n_layers) + if (ml->current_layer + 1 >= ml->n_layers) { - GtkTreeModel *tm; psppire_var_view_clear (PSPPIRE_VAR_VIEW (ml->var_view)); - tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ml->var_view)); - g_ptr_array_add (ml->layer, tm); - g_object_ref (tm); - ml->n_layers = ml->current_layer + 1; + add_new_layer (ml); + ml->n_layers = ml->current_layer + 2; } else { - GtkTreeModel *tm = g_ptr_array_index (ml->layer, ml->current_layer); + GtkTreeModel *tm = g_ptr_array_index (ml->layer, ml->current_layer + 1); gtk_tree_view_set_model (GTK_TREE_VIEW (ml->var_view), tm); } + ml->current_layer++; + psppire_means_layer_update (ml); } static void @@ -111,8 +138,23 @@ on_back (PsppireMeansLayer *ml) tm = g_ptr_array_index (ml->layer, ml->current_layer); gtk_tree_view_set_model (GTK_TREE_VIEW (ml->var_view), tm); + + psppire_means_layer_update (ml); } +void +psppire_means_layer_clear (PsppireMeansLayer *ml) +{ + psppire_var_view_clear (PSPPIRE_VAR_VIEW (ml->var_view)); + + ml->n_layers = 1; + ml->current_layer = 0; + ml->layer = g_ptr_array_new_full (3, g_object_unref); + + add_new_layer (ml); + + psppire_means_layer_update (ml); +} static void psppire_means_layer_init (PsppireMeansLayer *ml) @@ -126,7 +168,6 @@ psppire_means_layer_init (PsppireMeansLayer *ml) ml->forward = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD); ml->back = gtk_button_new_from_stock (GTK_STOCK_GO_BACK); ml->var_view = psppire_var_view_new (); - ml->selector = psppire_selector_new (); ml->label = gtk_label_new (""); g_signal_connect_swapped (ml->forward, "clicked", G_CALLBACK (on_forward), @@ -134,9 +175,6 @@ psppire_means_layer_init (PsppireMeansLayer *ml) g_signal_connect_swapped (ml->back, "clicked", G_CALLBACK (on_back), ml); - g_signal_connect_swapped (ml->selector, "selected", G_CALLBACK (update), ml); - g_signal_connect_swapped (ml->selector, "de-selected", G_CALLBACK (update), - ml); g_object_set (ml->var_view, "headers-visible", FALSE, NULL); g_object_set (sw, @@ -144,32 +182,20 @@ psppire_means_layer_init (PsppireMeansLayer *ml) "hscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL); - g_object_set (ml->selector, "dest-widget", ml->var_view, NULL); - g_signal_connect_swapped (ml->var_view, "notify::model", G_CALLBACK (update), ml); + g_signal_connect_swapped (ml->var_view, "notify::model", G_CALLBACK (psppire_means_layer_update), ml); gtk_box_pack_start (GTK_BOX (hbox_upper), ml->back, FALSE, FALSE, 5); gtk_box_pack_start (GTK_BOX (hbox_upper), ml->label, TRUE, FALSE, 5); gtk_box_pack_start (GTK_BOX (hbox_upper), ml->forward, FALSE, FALSE, 5); gtk_box_pack_start (GTK_BOX (hbox_lower), alignment, FALSE, FALSE, 5); - gtk_container_add (GTK_CONTAINER (alignment), ml->selector); gtk_box_pack_start (GTK_BOX (hbox_lower), sw, TRUE, TRUE, 5); gtk_container_add (GTK_CONTAINER (sw), ml->var_view); gtk_box_pack_start (GTK_BOX (ml), hbox_upper, FALSE, FALSE, 5); gtk_box_pack_start (GTK_BOX (ml), hbox_lower, TRUE, TRUE, 5); - - - ml->n_layers = 1; - ml->current_layer = 0; - ml->layer = g_ptr_array_new_full (3, g_object_unref); - - /* Add a model and take a reference to it */ - g_ptr_array_add (ml->layer, gtk_tree_view_get_model (GTK_TREE_VIEW (ml->var_view))); - g_object_ref (g_ptr_array_index (ml->layer, ml->current_layer)); - - update (ml); + psppire_means_layer_clear (ml); gtk_widget_show_all (hbox_upper); gtk_widget_show_all (hbox_lower); @@ -182,24 +208,18 @@ psppire_means_layer_new (void) } -void -psppire_means_layer_set_source (PsppireMeansLayer *ml, GtkWidget *w) -{ - g_object_set (ml->selector, "source-widget", w, NULL); -} -void -psppire_means_layer_clear (PsppireMeansLayer *ml) +GtkTreeModel * +psppire_means_layer_get_model_n (PsppireMeansLayer *ml, gint n) { - ml->n_layers = 1; - ml->current_layer = 0; - psppire_var_view_clear (PSPPIRE_VAR_VIEW (ml->var_view)); + return g_ptr_array_index (ml->layer, n); } GtkTreeModel * -psppire_means_layer_get_model_n (PsppireMeansLayer *ml, gint n) +psppire_means_layer_get_model (PsppireMeansLayer *ml) { - return g_ptr_array_index (ml->layer, n); + return g_ptr_array_index (ml->layer, ml->current_layer); } + diff --git a/src/ui/gui/psppire-means-layer.h b/src/ui/gui/psppire-means-layer.h index 7cd0f7b17f..ecf019d80d 100644 --- a/src/ui/gui/psppire-means-layer.h +++ b/src/ui/gui/psppire-means-layer.h @@ -64,7 +64,6 @@ struct _PsppireMeansLayer GtkWidget *label; GtkWidget *back; GtkWidget *forward; - GtkWidget *selector; }; @@ -77,11 +76,11 @@ GType psppire_means_layer_get_type (void); GType psppire_means_layer_model_get_type (void); GtkWidget * psppire_means_layer_new (void); -void psppire_means_layer_set_source (PsppireMeansLayer *ml, GtkWidget *w); - void psppire_means_layer_clear (PsppireMeansLayer *ml); GtkTreeModel *psppire_means_layer_get_model_n (PsppireMeansLayer *ml, gint n); +GtkTreeModel *psppire_means_layer_get_model (PsppireMeansLayer *ml); +void psppire_means_layer_update (PsppireMeansLayer *ml); G_END_DECLS diff --git a/src/ui/gui/psppire-selector.c b/src/ui/gui/psppire-selector.c index 82e2139976..17e173b5a5 100644 --- a/src/ui/gui/psppire-selector.c +++ b/src/ui/gui/psppire-selector.c @@ -59,9 +59,9 @@ #include #include "psppire-dictview.h" -#include "psppire-var-view.h" #include "psppire-dict.h" #include "psppire-select-dest.h" +#include "psppire-means-layer.h" #include @@ -530,6 +530,7 @@ on_dest_treeview_select (GtkTreeSelection *treeselection, gpointer data) set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE); } + /* Callback for source deselection, when the dest is GtkEntry */ static void de_select_selection_entry (PsppireSelector *selector) @@ -537,23 +538,47 @@ de_select_selection_entry (PsppireSelector *selector) gtk_entry_set_text (GTK_ENTRY (selector->dest), ""); } + +static void de_select_tree_model (GtkTreeSelection *selection, GtkTreeModel *model); + +/* Callback for source deselection, when the dest is PsppireMeansLayer */ +static void +de_select_selection_means_layer (PsppireSelector *selector) +{ + PsppireMeansLayer *ml = PSPPIRE_MEANS_LAYER (selector->dest); + GtkTreeView *tv = GTK_TREE_VIEW (ml->var_view); + GtkTreeSelection *selection = gtk_tree_view_get_selection (tv); + + GtkTreeModel *model = psppire_means_layer_get_model (ml); + + g_return_if_fail (selector->select_items); + + de_select_tree_model (selection, model); +} + /* Callback for source deselection, when the dest is GtkTreeView */ static void de_select_selection_tree_view (PsppireSelector *selector) { - GList *item; - GtkTreeSelection* selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->dest)); GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest)); + g_return_if_fail (selector->select_items); + + de_select_tree_model (selection, model); +} + +static void +de_select_tree_model (GtkTreeSelection *selection, GtkTreeModel *model) +{ + GList *item; + GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL); - g_return_if_fail (selector->select_items); - /* Convert paths to RowRefs */ for (item = g_list_first (selected_rows); item != NULL; @@ -616,6 +641,9 @@ de_select_selection (PsppireSelector *selector) else if ( GTK_IS_ENTRY (selector->dest)) de_select_selection_entry (selector); + else if ( PSPPIRE_IS_MEANS_LAYER (selector->dest)) + de_select_selection_means_layer (selector); + else g_assert_not_reached (); @@ -928,7 +956,18 @@ set_tree_view_dest (PsppireSelector *selector, G_CALLBACK (on_dest_model_changed), selector); } +static void +set_layer_dest (PsppireSelector *selector, + PsppireMeansLayer *dest) +{ + GtkTreeSelection* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dest->var_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + + + g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select), + selector); +} /* Callback for when the DEST GtkEntry is selected (clicked) */ @@ -1005,10 +1044,14 @@ update_subjects (PsppireSelector *selector) { set_tree_view_dest (selector, GTK_TREE_VIEW (selector->dest)); } - else if ( GTK_IS_ENTRY (selector->dest)) - set_entry_dest (selector, GTK_ENTRY (selector->dest)); - + { + set_entry_dest (selector, GTK_ENTRY (selector->dest)); + } + else if (PSPPIRE_IS_MEANS_LAYER (selector->dest)) + { + set_layer_dest (selector, PSPPIRE_MEANS_LAYER (selector->dest)); + } else if (GTK_IS_TEXT_VIEW (selector->dest)) { /* Nothing to be done */ @@ -1078,15 +1121,17 @@ GType psppire_selector_orientation_get_type (void) { static GType etype = 0; - if (etype == 0) { - static const GEnumValue values[] = { - { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" }, - { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" }, - { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" }, - { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" }, - { 0, NULL, NULL } - }; - etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values); - } + if (etype == 0) + { + static const GEnumValue values[] = + { + { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" }, + { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" }, + { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" }, + { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values); + } return etype; } diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c index a3f6a2aff6..93f3f99f7b 100644 --- a/src/ui/gui/psppire.c +++ b/src/ui/gui/psppire.c @@ -53,6 +53,7 @@ #include "ui/gui/psppire-syntax-window.h" #include "ui/gui/psppire-selector.h" #include "ui/gui/psppire-var-view.h" +#include "ui/gui/psppire-means-layer.h" #include "ui/gui/psppire-window-register.h" #include "ui/gui/widgets.h" #include "ui/source-init-opts.h" @@ -95,9 +96,11 @@ initialize (const char *data_file) journal_init (); textdomain (PACKAGE); + /* FIXME: This should be implemented with a GtkInterface */ psppire_selector_set_default_selection_func (GTK_TYPE_ENTRY, insert_source_row_into_entry); psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE, insert_source_row_into_tree_view); psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW, insert_source_row_into_tree_view); + psppire_selector_set_default_selection_func (PSPPIRE_TYPE_MEANS_LAYER, insert_source_row_into_layers); if (data_file) { -- 2.30.2