Fixed various memory leaks in GUI
[pspp-builds.git] / src / ui / gui / psppire-data-store.c
index 8f52cf7b08fc20e04ce25a5ab333ae76c406bf3b..4f548605f3dd008e6d56b903993df2a2cb502c58 100644 (file)
@@ -48,6 +48,7 @@ static void psppire_data_store_sheet_column_init (GSheetColumnIface *iface);
 static void psppire_data_store_sheet_row_init (GSheetRowIface *iface);
 
 static void psppire_data_store_finalize        (GObject           *object);
+static void psppire_data_store_dispose        (GObject           *object);
 
 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
                                          glong row, glong column);
@@ -137,6 +138,7 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
   object_class = (GObjectClass*) class;
 
   object_class->finalize = psppire_data_store_finalize;
+  object_class->dispose = psppire_data_store_dispose;
 
   signals [FONT_CHANGED] =
     g_signal_new ("font_changed",
@@ -159,26 +161,32 @@ psppire_data_store_get_var_count (const GSheetModel *model)
   return psppire_dict_get_var_cnt (store->dict);
 }
 
-casenumber
-psppire_data_store_get_case_count (PsppireDataStore *store)
+inline casenumber
+psppire_data_store_get_case_count (const PsppireDataStore *store)
 {
   return psppire_case_file_get_case_count (store->case_file);
 }
 
-static glong
-psppire_data_store_get_case_count_from_model (const GSheetModel *model)
+size_t
+psppire_data_store_get_value_count (const PsppireDataStore *store)
 {
-  const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+  return psppire_dict_get_value_cnt (store->dict);
+}
 
-  return psppire_case_file_get_case_count (store->case_file);
+inline casenumber
+psppire_data_store_get_case_count_wrapper (const GSheetModel *model)
+{
+  const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+  return psppire_data_store_get_case_count (store);
 }
 
 static void
 psppire_data_store_init (PsppireDataStore *data_store)
 {
   data_store->dict = 0;
-  data_store->case_file = 0;
+  data_store->case_file = NULL;
   data_store->width_of_m = 10;
+  data_store->dispose_has_run = FALSE;
 }
 
 const PangoFontDescription *
@@ -224,7 +232,7 @@ psppire_data_store_sheet_model_init (GSheetModelIface *iface)
   iface->get_font_desc = psppire_data_store_get_font_desc;
   iface->get_cell_border = NULL;
   iface->get_column_count = psppire_data_store_get_var_count;
-  iface->get_row_count = psppire_data_store_get_case_count_from_model;
+  iface->get_row_count = psppire_data_store_get_case_count_wrapper;
 }
 
 static
@@ -282,30 +290,27 @@ changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
 }
 
 
+/*
+   A callback which occurs after a variable has been deleted.
+ */
 static void
-delete_variables_callback (GObject *obj, gint var_num, gint n_vars, gpointer data)
+delete_variable_callback (GObject *obj, gint dict_index,
+                         gint case_index, gint val_cnt,
+                         gpointer data)
 {
-  PsppireDataStore *store ;
-
-  g_return_if_fail (data);
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_sheet_model_columns_deleted (G_SHEET_MODEL (store), var_num, n_vars);
+  g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
-                                  var_num, -1);
+                                  dict_index, -1);
 }
 
 
 static void
 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
 {
-  PsppireDataStore *store;
-
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
                                  var_num, 1);
@@ -353,13 +358,16 @@ static void
 dict_size_change_callback (GObject *obj,
                          gint posn, gint adjustment, gpointer data)
 {
-  PsppireDataStore *store ;
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
-  g_return_if_fail (data);
+  const struct variable *v = psppire_dict_get_variable (store->dict, posn);
 
-  store  = PSPPIRE_DATA_STORE (data);
+  const gint new_val_width = value_cnt_from_width (var_get_width (v));
 
-  psppire_case_file_insert_values (store->case_file, adjustment, posn);
+  if ( adjustment > 0 )
+    psppire_case_file_insert_values (store->case_file, adjustment,
+                                    new_val_width - adjustment +
+                                    var_get_case_index(v));
 }
 
 
@@ -385,34 +393,51 @@ psppire_data_store_new (PsppireDict *dict)
 
 
 void
-psppire_data_store_set_case_file (PsppireDataStore *data_store,
+psppire_data_store_set_case_file (PsppireDataStore *ds,
                                  PsppireCaseFile *cf)
 {
-  if ( data_store->case_file)
-    {
-      g_object_unref (data_store->case_file);
-    }
+  gint i;
+  if ( ds->case_file)  g_object_unref (ds->case_file);
+
 
-  data_store->case_file = cf;
+  ds->case_file = cf;
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (data_store),
+  g_sheet_model_range_changed (G_SHEET_MODEL (ds),
                               -1, -1, -1, -1);
 
+  for (i = 0 ; i < n_cf_signals ; ++i )
+    {
+      if ( ds->cf_handler_id [i] > 0 )
+       g_signal_handler_disconnect (ds->case_file,
+                                    ds->cf_handler_id[i]);
+    }
 
-  g_signal_connect (data_store->case_file, "cases-deleted",
-                  G_CALLBACK (delete_cases_callback),
-                  data_store);
 
-  g_signal_connect (data_store->case_file, "case-inserted",
-                  G_CALLBACK (insert_case_callback),
-                  data_store);
+  if ( ds->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       if ( ds->dict_handler_id [i] > 0)
+         {
+           g_signal_handler_unblock (ds->dict,
+                                     ds->dict_handler_id[i]);
+         }
+      }
 
+  ds->cf_handler_id [CASES_DELETED] =
+    g_signal_connect (ds->case_file, "cases-deleted",
+                     G_CALLBACK (delete_cases_callback),
+                     ds);
 
-  g_signal_connect (data_store->case_file, "case-changed",
-                  G_CALLBACK (changed_case_callback),
-                  data_store);
-}
+  ds->cf_handler_id [CASE_INSERTED] =
+    g_signal_connect (ds->case_file, "case-inserted",
+                     G_CALLBACK (insert_case_callback),
+                     ds);
 
+  ds->cf_handler_id [CASE_CHANGED] =
+    g_signal_connect (ds->case_file, "case-changed",
+                     G_CALLBACK (changed_case_callback),
+                     ds);
+}
 
 
 /**
@@ -426,29 +451,59 @@ psppire_data_store_set_case_file (PsppireDataStore *data_store,
 void
 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
 {
-  data_store->dict = dict;
+  int i;
 
-  g_signal_connect (dict, "variable-inserted",
-                  G_CALLBACK (insert_variable_callback),
-                  data_store);
+  /* Disconnect any existing handlers */
+  if ( data_store->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       g_signal_handler_disconnect (data_store->dict,
+                                    data_store->dict_handler_id[i]);
+      }
 
-  g_signal_connect (dict, "variables-deleted",
-                  G_CALLBACK (delete_variables_callback),
-                  data_store);
+  data_store->dict = dict;
 
-  g_signal_connect (dict, "variable-changed",
-                  G_CALLBACK (variable_changed_callback),
-                  data_store);
+  if ( dict != NULL)
+    {
+
+      data_store->dict_handler_id [VARIABLE_INSERTED] =
+       g_signal_connect (dict, "variable-inserted",
+                         G_CALLBACK (insert_variable_callback),
+                         data_store);
+
+      data_store->dict_handler_id [VARIABLE_DELETED] =
+       g_signal_connect (dict, "variable-deleted",
+                         G_CALLBACK (delete_variable_callback),
+                         data_store);
+
+      data_store->dict_handler_id [VARIABLE_CHANGED] =
+       g_signal_connect (dict, "variable-changed",
+                         G_CALLBACK (variable_changed_callback),
+                         data_store);
+
+      data_store->dict_handler_id [SIZE_CHANGED] =
+       g_signal_connect (dict, "dict-size-changed",
+                         G_CALLBACK (dict_size_change_callback),
+                         data_store);
+    }
 
 
-  g_signal_connect (dict, "dict-size-changed",
-                   G_CALLBACK (dict_size_change_callback),
-                   data_store);
 
   /* The entire model has changed */
   g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
+
+
+  if ( data_store->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       if ( data_store->dict_handler_id [i] > 0)
+         {
+           g_signal_handler_block (data_store->dict,
+                                   data_store->dict_handler_id[i]);
+         }
+      }
 }
 
 static void
@@ -459,6 +514,24 @@ psppire_data_store_finalize (GObject *object)
   (* parent_class->finalize) (object);
 }
 
+
+static void
+psppire_data_store_dispose (GObject *object)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
+
+  if (ds->dispose_has_run)
+    return;
+
+  if (ds->case_file) g_object_unref (ds->case_file);
+
+  /* must chain up */
+  (* parent_class->dispose) (object);
+
+  ds->dispose_has_run = TRUE;
+}
+
+
 gboolean
 psppire_data_store_delete_cases (PsppireDataStore *ds,
                                 casenumber first, casenumber count)
@@ -483,6 +556,8 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
 
   g_return_val_if_fail (val_cnt > 0, FALSE);
 
+  g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
+
   case_create (&cc, val_cnt);
 
   memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
@@ -673,8 +748,22 @@ psppire_data_store_clear (PsppireDataStore *data_store)
 struct casereader *
 psppire_data_store_get_reader (PsppireDataStore *ds)
 {
+  int i;
   struct casereader *reader ;
 
+  for (i = 0 ; i < n_cf_signals ; ++i )
+    {
+      g_signal_handler_disconnect (ds->case_file, ds->cf_handler_id[i]);
+      ds->cf_handler_id[i] = 0 ;
+    }
+
+  if ( ds->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       g_signal_handler_block (ds->dict,
+                               ds->dict_handler_id[i]);
+      }
+
   reader = psppire_case_file_make_reader (ds->case_file);
 
   return reader;
@@ -815,11 +904,29 @@ geometry_get_row_count (const GSheetRow *geom, gpointer data)
   return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
 }
 
+#define ROW_HEIGHT 25
 
 static gint
 geometry_get_height (const GSheetRow *geom, glong unit, gpointer data)
 {
-  return 25;
+  return ROW_HEIGHT;
+}
+
+static guint
+geometry_get_top_ypixel (const GSheetRow *geo, glong row, gpointer data)
+{
+  return row * ROW_HEIGHT;
+}
+
+static glong
+geometry_pixel_to_row (const GSheetRow *geo, guint pixel, gpointer data)
+{
+  glong row  = pixel / ROW_HEIGHT;
+
+  if (row >= geometry_get_row_count (geo, data))
+    row = geometry_get_row_count (geo, data) - 1;
+
+  return row;
 }
 
 
@@ -853,7 +960,6 @@ geometry_get_row_button_label (const GSheetRow *geom, glong unit, gpointer data)
   return text;
 }
 
-
 static void
 psppire_data_store_sheet_row_init (GSheetRowIface *iface)
 {
@@ -863,6 +969,7 @@ psppire_data_store_sheet_row_init (GSheetRowIface *iface)
   iface->set_height = 0;
   iface->get_visibility = always_true;
   iface->get_sensitivity = geometry_get_row_sensitivity;
-
+  iface->top_ypixel = geometry_get_top_ypixel;
+  iface->pixel_to_row = geometry_pixel_to_row;
   iface->get_button_label = geometry_get_row_button_label;
 }