Prevent widths less than 1
[pspp-builds.git] / src / ui / gui / psppire-data-editor.c
index ac90bd1df6ca82d294e791969c288bc39b911e17..0fe4f68091b1d8395d8d7c8e0e1c437b56035e63 100644 (file)
@@ -23,8 +23,7 @@
 
 #include <language/syntax-string-source.h>
 #include "psppire-data-store.h"
-#include <gtksheet/psppire-axis-hetero.h>
-#include <gtksheet/psppire-axis-uniform.h>
+#include <gtksheet/psppire-axis-impl.h>
 #include "helper.h"
 
 #include <gtksheet/gtkxpaned.h>
@@ -109,8 +108,12 @@ psppire_data_editor_finalize (GObject *obj)
 
 
 
-static void popup_variable_menu (GtkSheet *sheet, gint column,
-                                GdkEventButton *event, gpointer data);
+static void popup_variable_column_menu (GtkSheet *sheet, gint column,
+                                       GdkEventButton *event, gpointer data);
+
+static void popup_variable_row_menu (GtkSheet *sheet, gint row,
+                                    GdkEventButton *event, gpointer data);
+
 
 static void popup_cases_menu (GtkSheet *sheet, gint row,
                              GdkEventButton *event, gpointer data);
@@ -118,6 +121,7 @@ static void popup_cases_menu (GtkSheet *sheet, gint row,
 
 
 
+
 /* Callback which occurs when the data sheet's column title
    is double clicked */
 static gboolean
@@ -193,6 +197,7 @@ enum
     PROP_0,
     PROP_DATA_STORE,
     PROP_VAR_STORE,
+    PROP_VS_ROW_MENU,
     PROP_DS_COLUMN_MENU,
     PROP_DS_ROW_MENU,
     PROP_VALUE_LABELS,
@@ -208,48 +213,40 @@ enum
 static void
 new_data_callback (PsppireDataStore *ds, gpointer data)
 {
-  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
   gint i;
-  for (i = 0 ; i < 4 ; ++i)
-    {
-      PsppireAxisUniform *vaxis;
-      casenumber n_cases =  psppire_case_file_get_case_count (ds->case_file);
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
 
-      g_object_get (de->data_sheet[i], "vertical-axis", &vaxis, NULL);
+  casenumber n_cases =  psppire_data_store_get_case_count (ds);
 
-      psppire_axis_uniform_set_count (vaxis, n_cases);
+  for (i = 0; i < 2; ++i)
+    {
+      psppire_axis_impl_clear (de->vaxis[i]);
+      psppire_axis_impl_append_n (de->vaxis[i], n_cases, DEFAULT_ROW_HEIGHT);
     }
 }
 
 static void
-new_variables_callback (PsppireDict *dict, gpointer data)
+case_inserted_callback (PsppireDataStore *ds, gint before, gpointer data)
 {
-  gint v, i;
+  gint i;
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
 
-  PsppireAxisHetero *vaxis;
-  g_object_get (de->var_sheet, "vertical-axis", &vaxis, NULL);
-
-  psppire_axis_hetero_clear (vaxis);
+  for (i = 0; i < 2; ++i)
+    psppire_axis_impl_insert (de->vaxis[i], before, DEFAULT_ROW_HEIGHT);
+}
 
-  for (v = 0 ; v < psppire_dict_get_var_cnt (dict); ++v)
-    psppire_axis_hetero_append (vaxis, DEFAULT_ROW_HEIGHT);
 
-  for (i = 0 ; i < 4 ; ++i)
-    {
-      PsppireAxisHetero *haxis;
-      g_object_get (de->data_sheet[i], "horizontal-axis", &haxis, NULL);
+static void
+cases_deleted_callback (PsppireDataStore *ds, gint first, gint n_cases, gpointer data)
+{
+  gint i;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
 
-      psppire_axis_hetero_clear (haxis);
+  for (i = 0; i < 2; ++i)
+    psppire_axis_impl_delete (de->vaxis[0], first, n_cases);
+}
 
-      for (v = 0 ; v < psppire_dict_get_var_cnt (dict); ++v)
-       {
-         const struct variable *var = psppire_dict_get_variable (dict, v);
 
-         psppire_axis_hetero_append (haxis, 10 * var_get_display_width (var));
-       }
-    }
-}
 
 /* Return the width (in pixels) of an upper case M when rendered in the
    current font of W
@@ -267,70 +264,96 @@ width_of_m (GtkWidget *w)
   return rect.width;
 }
 
+/* Callback for the axis' resize signal.
+   Changes the variable's display width */
 static void
-insert_variable_callback (PsppireDict *dict, gint x, gpointer data)
+rewidth_variable (GtkWidget *w, gint unit, glong size)
 {
-  gint i;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (w);
+
+  const PsppireDict *dict = de->data_store->dict;
+  struct variable *var = psppire_dict_get_variable (dict, unit);
+
+  var_set_display_width (var, size / (float) width_of_m (w));
+}
+
 
+static void
+new_variables_callback (PsppireDict *dict, gpointer data)
+{
+  gint v;
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+  gint m_width = width_of_m (GTK_WIDGET (de));
 
-  gint m_width  = width_of_m (GTK_WIDGET (de));
+  PsppireAxisImpl *vaxis;
+  g_object_get (de->var_sheet, "vertical-axis", &vaxis, NULL);
 
-  PsppireAxisHetero *var_vaxis;
-  g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
+  psppire_axis_impl_clear (vaxis);
+  psppire_axis_impl_append_n (vaxis, 1 + psppire_dict_get_var_cnt (dict), DEFAULT_ROW_HEIGHT);
+
+  g_signal_connect_swapped (de->haxis, "resize-unit",
+                           G_CALLBACK (rewidth_variable), de);
 
-  psppire_axis_hetero_insert (var_vaxis, DEFAULT_ROW_HEIGHT, x);
+  psppire_axis_impl_clear (de->haxis);
 
-  for (i = 0 ; i < 4 ; ++i)
+  for (v = 0 ; v < psppire_dict_get_var_cnt (dict); ++v)
     {
-      const struct variable *var = psppire_dict_get_variable (dict, x);
-      PsppireAxisHetero *haxis;
-      g_object_get (de->data_sheet[i], "horizontal-axis", &haxis, NULL);
+      const struct variable *var = psppire_dict_get_variable (dict, v);
 
-      psppire_axis_hetero_insert (haxis, m_width * var_get_display_width (var), x);
+      psppire_axis_impl_append (de->haxis, m_width * var_get_display_width (var));
     }
 }
 
+static void
+insert_variable_callback (PsppireDict *dict, gint x, gpointer data)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+
+  gint m_width  = width_of_m (GTK_WIDGET (de));
+
+  PsppireAxisImpl *var_vaxis;
+
+  const struct variable *var = psppire_dict_get_variable (dict, x);
+
+  g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
+
+  psppire_axis_impl_insert (var_vaxis, x, DEFAULT_ROW_HEIGHT);
+
+
+  psppire_axis_impl_insert (de->haxis, x, m_width * var_get_display_width (var));
+}
+
 
 static void
 delete_variable_callback (PsppireDict *dict, gint posn,
                          gint x UNUSED, gint y UNUSED, gpointer data)
 {
-  gint i;
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
 
-  PsppireAxisHetero *var_vaxis;
+  PsppireAxisImpl *var_vaxis;
   g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
 
-  psppire_axis_hetero_remove (var_vaxis, posn);
+  psppire_axis_impl_delete (var_vaxis, posn, 1);
 
-  for (i = 0 ; i < 4 ; ++i)
-    {
-      PsppireAxisHetero *haxis;
-      g_object_get (de->data_sheet[i], "horizontal-axis", &haxis, NULL);
-
-      psppire_axis_hetero_remove (haxis, posn);
-    }
+  psppire_axis_impl_delete (de->haxis, posn, 1);
 }
 
 
 static void
 rewidth_variable_callback (PsppireDict *dict, gint posn, gpointer data)
 {
-  gint i;
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
   gint m_width = width_of_m (GTK_WIDGET (de));
 
-  for (i = 0 ; i < 4 ; ++i)
-    {
-      const struct variable *var = psppire_dict_get_variable (dict, posn);
-      PsppireAxisHetero *haxis;
-      g_object_get (de->data_sheet[i], "horizontal-axis", &haxis, NULL);
+  const struct variable *var = psppire_dict_get_variable (dict, posn);
 
-      psppire_axis_hetero_resize_unit (haxis,
-                                      m_width *
-                                      var_get_display_width (var), posn);
-    }
+  gint var_width = var_get_display_width (var);
+
+  /* Don't allow zero width */
+  if ( var_width < 1 )
+    var_width = 1;
+
+  psppire_axis_impl_resize (de->haxis, posn, m_width * var_width);
 }
 
 
@@ -358,12 +381,27 @@ psppire_data_editor_set_property (GObject         *object,
                      "model", de->data_store,
                      NULL);
 
-      g_signal_connect (de->data_store->dict, "backend-changed",   G_CALLBACK (new_variables_callback), de);
-      g_signal_connect (de->data_store->dict, "variable-inserted", G_CALLBACK (insert_variable_callback), de);
-      g_signal_connect (de->data_store->dict, "variable-deleted",  G_CALLBACK (delete_variable_callback), de);
-      g_signal_connect (de->data_store->dict, "variable-display-width-changed",  G_CALLBACK (rewidth_variable_callback), de);
+      g_signal_connect (de->data_store->dict, "backend-changed",
+                       G_CALLBACK (new_variables_callback), de);
+
+      g_signal_connect (de->data_store->dict, "variable-inserted",
+                       G_CALLBACK (insert_variable_callback), de);
+
+      g_signal_connect (de->data_store->dict, "variable-deleted",
+                       G_CALLBACK (delete_variable_callback), de);
+
+      g_signal_connect (de->data_store->dict, "variable-display-width-changed",
+                       G_CALLBACK (rewidth_variable_callback), de);
+
+      g_signal_connect (de->data_store, "backend-changed",
+                       G_CALLBACK (new_data_callback), de);
+
+      g_signal_connect (de->data_store, "case-inserted",
+                       G_CALLBACK (case_inserted_callback), de);
+
+      g_signal_connect (de->data_store, "cases-deleted",
+                       G_CALLBACK (cases_deleted_callback), de);
 
-      g_signal_connect (de->data_store, "backend-changed", G_CALLBACK (new_data_callback), de);
       break;
     case PROP_VAR_STORE:
       if ( de->var_store) g_object_unref (de->var_store);
@@ -374,12 +412,20 @@ psppire_data_editor_set_property (GObject         *object,
                    "model", de->var_store,
                    NULL);
       break;
+    case PROP_VS_ROW_MENU:
+      {
+       GObject *menu = g_value_get_object (value);
+
+       g_signal_connect (de->var_sheet, "button-event-row",
+                         G_CALLBACK (popup_variable_row_menu), menu);
+      }
+      break;
     case PROP_DS_COLUMN_MENU:
       {
        GObject *menu = g_value_get_object (value);
 
        g_signal_connect (de->data_sheet[0], "button-event-column",
-                         G_CALLBACK (popup_variable_menu), menu);
+                         G_CALLBACK (popup_variable_column_menu), menu);
       }
       break;
     case PROP_DS_ROW_MENU:
@@ -482,7 +528,8 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
   GParamSpec *data_store_spec ;
   GParamSpec *var_store_spec ;
   GParamSpec *column_menu_spec;
-  GParamSpec *row_menu_spec;
+  GParamSpec *ds_row_menu_spec;
+  GParamSpec *vs_row_menu_spec;
   GParamSpec *value_labels_spec;
   GParamSpec *current_case_spec;
   GParamSpec *current_var_spec;
@@ -530,7 +577,7 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
                                    column_menu_spec);
 
 
-  row_menu_spec =
+  ds_row_menu_spec =
     g_param_spec_object ("datasheet-row-menu",
                         "Data Sheet Row Menu",
                         "A menu to be displayed when button 3 is pressed in the data sheet's row title buttons",
@@ -539,7 +586,20 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
 
   g_object_class_install_property (object_class,
                                    PROP_DS_ROW_MENU,
-                                   row_menu_spec);
+                                   ds_row_menu_spec);
+
+
+  vs_row_menu_spec =
+    g_param_spec_object ("varsheet-row-menu",
+                        "Variable Sheet Row Menu",
+                        "A menu to be displayed when button 3 is pressed in the variable sheet's row title buttons",
+                        GTK_TYPE_MENU,
+                        G_PARAM_WRITABLE);
+
+  g_object_class_install_property (object_class,
+                                   PROP_VS_ROW_MENU,
+                                   vs_row_menu_spec);
+
 
   value_labels_spec =
     g_param_spec_boolean ("value-labels",
@@ -750,10 +810,11 @@ on_map (GtkWidget *w)
 
 static void
 init_sheet (PsppireDataEditor *de, int i,
-           GtkAdjustment *hadj, GtkAdjustment *vadj)
+           GtkAdjustment *hadj, GtkAdjustment *vadj,
+           PsppireAxisImpl *vaxis,
+           PsppireAxisImpl *haxis
+           )
 {
-  PsppireAxisHetero *haxis = psppire_axis_hetero_new ();
-  PsppireAxisUniform *vaxis = psppire_axis_uniform_new ();
   de->sheet_bin[i] = gtk_scrolled_window_new (hadj, vadj);
 
   de->data_sheet[i] = gtk_sheet_new (NULL);
@@ -783,38 +844,46 @@ init_sheet (PsppireDataEditor *de, int i,
 static void
 init_data_sheet (PsppireDataEditor *de)
 {
-  GtkAdjustment *va0, *ha0;
-  GtkAdjustment *va1, *ha1;
+  GtkAdjustment *vadj0, *hadj0;
+  GtkAdjustment *vadj1, *hadj1;
   GtkWidget *sheet ;
 
+  de->vaxis[0] = psppire_axis_impl_new ();
+  de->vaxis[1] = psppire_axis_impl_new ();
+
+  /* Txoxovhere's only one horizontal axis, since the
+     column widths are parameters of the variables */
+  de->haxis = psppire_axis_impl_new ();
+
+
   de->split = TRUE;
   de->paned = gtk_xpaned_new ();
 
-  init_sheet (de, 0, NULL, NULL);
+  init_sheet (de, 0, NULL, NULL, de->vaxis[0], de->haxis);
   gtk_widget_show (de->sheet_bin[0]);
-  va0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
-  ha0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
+  vadj0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
+  hadj0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
 
   g_object_set (de->sheet_bin[0], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
   g_object_set (de->sheet_bin[0], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
 
-  init_sheet (de, 1, NULL, va0);
+  init_sheet (de, 1, NULL, vadj0, de->vaxis[0], de->haxis);
   gtk_widget_show (de->sheet_bin[1]);
   sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[1]));
   gtk_sheet_hide_row_titles (GTK_SHEET (sheet));
-  ha1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
+  hadj1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
   g_object_set (de->sheet_bin[1], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
   g_object_set (de->sheet_bin[1], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
 
-  init_sheet (de, 2, ha0, NULL);
+  init_sheet (de, 2, hadj0, NULL, de->vaxis[1], de->haxis);
   gtk_widget_show (de->sheet_bin[2]);
   sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[2]));
   gtk_sheet_hide_column_titles (GTK_SHEET (sheet));
   g_object_set (de->sheet_bin[2], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
   g_object_set (de->sheet_bin[2], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
-  va1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
+  vadj1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
 
-  init_sheet (de, 3, ha1, va1);
+  init_sheet (de, 3, hadj1, vadj1, de->vaxis[1], de->haxis);
   gtk_widget_show (de->sheet_bin[3]);
   sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[3]));
   gtk_sheet_hide_column_titles (GTK_SHEET (sheet));
@@ -1096,7 +1165,7 @@ psppire_data_editor_clip_cut (PsppireDataEditor *de)
 /* Popup menu related stuff */
 
 static void
-popup_variable_menu (GtkSheet *sheet, gint column,
+popup_variable_column_menu (GtkSheet *sheet, gint column,
                     GdkEventButton *event, gpointer data)
 {
   GtkMenu *menu = GTK_MENU (data);
@@ -1118,6 +1187,29 @@ popup_variable_menu (GtkSheet *sheet, gint column,
 }
 
 
+static void
+popup_variable_row_menu (GtkSheet *sheet, gint row,
+                    GdkEventButton *event, gpointer data)
+{
+  GtkMenu *menu = GTK_MENU (data);
+
+  PsppireVarStore *var_store =
+    PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
+
+  const struct variable *v =
+    psppire_dict_get_variable (var_store->dict, row);
+
+  if ( v && event->button == 3)
+    {
+      gtk_sheet_select_row (sheet, row);
+
+      gtk_menu_popup (menu,
+                     NULL, NULL, NULL, NULL,
+                     event->button, event->time);
+    }
+}
+
+
 static void
 popup_cases_menu (GtkSheet *sheet, gint row,
                  GdkEventButton *event, gpointer data)
@@ -1514,7 +1606,7 @@ data_sheet_set_clip (GtkSheet *sheet)
     {
       struct ccase old;
 
-      if (psppire_case_file_get_case (ds->case_file, i, &old))
+      if (psppire_data_store_get_case (ds, i, &old))
         {
           struct ccase new;