Hold the thread lock in a timer callback
[pspp-builds.git] / lib / gtk-contrib / psppire-sheet.c
index dac561a134b9b2ad99aac73c16699edab2852535..3db8fd5bd0b558878036f069fd9ef7a2d74478b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 2006, 2008 Free Software Foundation
+   Copyright (C) 2006, 2008, 2009 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
@@ -69,7 +69,9 @@
 #include "psppire-sheet.h"
 #include <ui/gui/psppire-marshal.h>
 #include <ui/gui/sheet/psppire-sheetmodel.h>
+#include <ui/gui/sheet/psppire-axis.h>
 #include <libpspp/misc.h>
+
 #include <math.h>
 
 /* sheet flags */
@@ -476,6 +478,9 @@ static void psppire_sheet_size_request               (GtkWidget *widget,
 static void psppire_sheet_size_allocate                 (GtkWidget *widget,
                                                  GtkAllocation *allocation);
 
+static gboolean psppire_sheet_focus_in               (GtkWidget     *widget,
+                                                     GdkEventFocus *event);
+
 /* Sheet queries */
 
 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
@@ -694,6 +699,7 @@ enum
     PROP_0,
     PROP_VAXIS,
     PROP_HAXIS,
+    PROP_CELL_PADDING,
     PROP_MODEL
   };
 
@@ -753,6 +759,7 @@ psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
     g_object_ref (sheet->vaxis);
 }
 
+static const GtkBorder default_cell_padding = { 3, 3, 2, 2 };
 
 static void
 psppire_sheet_set_property (GObject         *object,
@@ -765,11 +772,37 @@ psppire_sheet_set_property (GObject         *object,
 
   switch (prop_id)
     {
+    case PROP_CELL_PADDING:
+      if ( sheet->cell_padding)
+       g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
+
+      sheet->cell_padding = g_value_dup_boxed (value);
+
+     if (NULL == sheet->cell_padding)
+       sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
+                                          &default_cell_padding);
+
+     if (sheet->vaxis)
+       g_object_set (sheet->vaxis, "padding",
+                    sheet->cell_padding->top + sheet->cell_padding->bottom,
+                    NULL);
+
+     if (sheet->haxis)
+       g_object_set (sheet->haxis, "padding",
+                    sheet->cell_padding->left + sheet->cell_padding->right,
+                    NULL);
+      break;
     case PROP_VAXIS:
       psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
+      g_object_set (sheet->vaxis, "padding",
+                   sheet->cell_padding->top + sheet->cell_padding->bottom,
+                   NULL);
       break;
     case PROP_HAXIS:
       psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
+      g_object_set (sheet->haxis, "padding",
+                   sheet->cell_padding->left + sheet->cell_padding->right,
+                   NULL);
       break;
     case PROP_MODEL:
       psppire_sheet_set_model (sheet, g_value_get_pointer (value));
@@ -790,6 +823,9 @@ psppire_sheet_get_property (GObject         *object,
 
   switch (prop_id)
     {
+    case PROP_CELL_PADDING:
+      g_value_set_boxed (value, sheet->cell_padding);
+      break;
     case PROP_VAXIS:
       g_value_set_pointer (value, sheet->vaxis);
       break;
@@ -814,6 +850,7 @@ psppire_sheet_class_init (PsppireSheetClass *klass)
   GParamSpec *haxis_spec ;
   GParamSpec *vaxis_spec ;
   GParamSpec *model_spec ;
+  GParamSpec *cell_padding_spec ;
 
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
@@ -1010,10 +1047,17 @@ psppire_sheet_class_init (PsppireSheetClass *klass)
   container_class->add = NULL;
   container_class->remove = NULL;
   container_class->forall = psppire_sheet_forall;
+  container_class->set_focus_child = NULL;
 
   object_class->dispose = psppire_sheet_dispose;
   object_class->finalize = psppire_sheet_finalize;
 
+  cell_padding_spec =
+    g_param_spec_boxed ("cell-padding",
+                       "Cell Padding",
+                       "The space between a cell's contents and its border",
+                       GTK_TYPE_BORDER,
+                       G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
 
   vaxis_spec =
     g_param_spec_pointer ("vertical-axis",
@@ -1045,6 +1089,10 @@ psppire_sheet_class_init (PsppireSheetClass *klass)
                                    PROP_HAXIS,
                                    haxis_spec);
 
+  g_object_class_install_property (object_class,
+                                   PROP_CELL_PADDING,
+                                   cell_padding_spec);
+
   g_object_class_install_property (object_class,
                                    PROP_MODEL,
                                    model_spec);
@@ -1064,7 +1112,7 @@ psppire_sheet_class_init (PsppireSheetClass *klass)
   widget_class->expose_event = psppire_sheet_expose;
   widget_class->size_request = psppire_sheet_size_request;
   widget_class->size_allocate = psppire_sheet_size_allocate;
-  widget_class->focus_in_event = NULL;
+  widget_class->focus_in_event = psppire_sheet_focus_in;
   widget_class->focus_out_event = NULL;
 
   klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
@@ -1748,6 +1796,9 @@ psppire_sheet_dispose  (GObject *object)
 
   sheet->dispose_has_run = TRUE;
 
+  if ( sheet->cell_padding)
+    g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
+
   if (sheet->model) g_object_unref (sheet->model);
   if (sheet->vaxis) g_object_unref (sheet->vaxis);
   if (sheet->haxis) g_object_unref (sheet->haxis);
@@ -2094,6 +2145,13 @@ psppire_sheet_unmap (GtkWidget *widget)
     gtk_widget_unmap (sheet->button);
 }
 
+/* get cell attributes of the given cell */
+/* TRUE means that the cell is currently allocated */
+static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
+                                             gint row, gint col,
+                                             PsppireSheetCellAttr *attributes);
+
+
 
 static void
 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
@@ -2162,6 +2220,19 @@ psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
   if ( !pango_font_description_get_size_is_absolute (font_desc))
     font_height /= PANGO_SCALE;
 
+
+  if ( sheet->cell_padding )
+    {
+      area.x += sheet->cell_padding->left;
+      area.width -= sheet->cell_padding->right
+       + sheet->cell_padding->left;
+
+      area.y += sheet->cell_padding->top;
+      area.height -= sheet->cell_padding->bottom
+       +
+       sheet->cell_padding->top;
+    }
+
   /* Centre the text vertically */
   area.y += (area.height - font_height) / 2.0;
 
@@ -3731,6 +3802,8 @@ motion_timeout_callback (gpointer data)
   PsppireSheet *sheet = PSPPIRE_SHEET (data);
   gint x, y;
   gint row, column;
+
+  gdk_threads_enter ();
   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
 
   if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
@@ -3754,6 +3827,7 @@ motion_timeout_callback (gpointer data)
        }
     }
 
+  gdk_threads_leave ();
   return FALSE;
 }
 
@@ -4052,6 +4126,19 @@ psppire_sheet_crossing_notify (GtkWidget *widget,
   return TRUE;
 }
 
+
+static gboolean
+psppire_sheet_focus_in (GtkWidget     *w,
+                       GdkEventFocus *event)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (w);
+
+  gtk_widget_grab_focus (sheet->entry_widget);
+
+  return TRUE;
+}
+
+
 static void
 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
 {
@@ -4185,12 +4272,29 @@ step_sheet (PsppireSheet *sheet, GtkScrollType dir)
     case GTK_SCROLL_STEP_LEFT:
       new_cell.col--;
       break;
+    case GTK_SCROLL_STEP_FORWARD:
+      new_cell.col++;
+      if (new_cell.col >=
+         psppire_sheet_model_get_column_count (sheet->model))
+       {
+         new_cell.col = 0;
+         new_cell.row++;
+       }
+      break;
+    case GTK_SCROLL_STEP_BACKWARD:
+      new_cell.col--;
+      if (new_cell.col < 0)
+       {
+         new_cell.col =
+           psppire_sheet_model_get_column_count (sheet->model) - 1;
+         new_cell.row--;
+       }
+      break;
     default:
       g_assert_not_reached ();
       break;
     }
 
-
   g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
                 &sheet->active_cell,
                 &new_cell,
@@ -4268,10 +4372,14 @@ psppire_sheet_key_press (GtkWidget *widget,
   switch (key->keyval)
     {
     case GDK_Tab:
+      step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
+      break;
     case GDK_Right:
       step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
       break;
     case GDK_ISO_Left_Tab:
+      step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
+      break;
     case GDK_Left:
       step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
       break;
@@ -4584,8 +4692,6 @@ set_entry_widget_font (PsppireSheet *sheet)
   gtk_widget_modify_style (sheet->entry_widget, style);
 }
 
-
-
 static void
 create_sheet_entry (PsppireSheet *sheet)
 {
@@ -4711,6 +4817,26 @@ draw_button (PsppireSheet *sheet, GdkWindow *window,
                   allocation.x, allocation.y,
                   allocation.width, allocation.height);
 
+  if ( button->overstruck)
+    {
+      GdkPoint points[2] = {
+       {allocation.x,  allocation.y},
+       {allocation.x + allocation.width,
+        allocation.y + allocation.height}
+      };
+
+      gtk_paint_polygon (sheet->button->style,
+                        window,
+                        button->state,
+                        shadow_type,
+                        NULL,
+                        GTK_WIDGET (sheet),
+                        "button",
+                        points,
+                        2,
+                        TRUE);
+    }
+
   if (button->label_visible)
     {
       text_height = DEFAULT_ROW_HEIGHT -
@@ -5156,7 +5282,9 @@ set_column_width (PsppireSheet *sheet,
   if ( width <= 0)
     return;
 
-  psppire_axis_resize (sheet->haxis, column, width);
+  psppire_axis_resize (sheet->haxis, column,
+                      width - sheet->cell_padding->left -
+                      sheet->cell_padding->right);
 
   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
     {
@@ -5181,7 +5309,9 @@ set_row_height (PsppireSheet *sheet,
   if (height <= 0)
     return;
 
-  psppire_axis_resize (sheet->vaxis, row, height);
+  psppire_axis_resize (sheet->vaxis, row,
+                      height - sheet->cell_padding->top -
+                      sheet->cell_padding->bottom);
 
   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
     {
@@ -5192,7 +5322,7 @@ set_row_height (PsppireSheet *sheet,
     }
 }
 
-gboolean
+static gboolean
 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
                          PsppireSheetCellAttr *attr)
 {
@@ -5215,8 +5345,6 @@ psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
   attr->border.mask = 0;
   attr->border.color = GTK_WIDGET (sheet)->style->black;
 
-  attr->is_editable = psppire_sheet_model_is_editable (sheet->model, row, col);
-
   colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
   fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
   if ( fg )
@@ -5299,6 +5427,7 @@ psppire_sheet_button_new (void)
   button->label = NULL;
   button->label_visible = TRUE;
   button->justification = GTK_JUSTIFY_FILL;
+  button->overstruck = FALSE;
 
   return button;
 }