Added a tooltip like feature to display variables' labels when the mouse hovers
authorJohn Darrington <john@darrington.wattle.id.au>
Sat, 7 Jul 2007 01:52:44 +0000 (01:52 +0000)
committerJohn Darrington <john@darrington.wattle.id.au>
Sat, 7 Jul 2007 01:52:44 +0000 (01:52 +0000)
over a column title in the datasheet.

lib/gtksheet/ChangeLog
lib/gtksheet/gsheet-column-iface.c
lib/gtksheet/gsheet-column-iface.h
lib/gtksheet/gsheet-row-iface.c
lib/gtksheet/gsheet-row-iface.h
lib/gtksheet/gtksheet.c
lib/gtksheet/gtksheet.h
src/ui/gui/ChangeLog
src/ui/gui/psppire-data-store.c

index cb859a397eb9d6f3c28d9f32bb159573311e2e81..5e96837c3b1507861e228222250a806f46412016 100644 (file)
@@ -1,3 +1,9 @@
+07 July 2007 John Darrington <john@darrington.wattle.id.au>
+        
+       * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c
+       gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle"
+       feature on row/column titles, which shows tooltip-like popups.  
+
 03 July 2007 John Darrington <john@darrington.wattle.id.au>
 
        * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature 
index dfb95a9dce8e3178df31824b254cb8c832eba9e1..0ad462f9c097bd745eefb35f44e82b3a43166ae4 100644 (file)
@@ -176,6 +176,16 @@ g_sheet_column_get_justification(const GSheetColumn *column,
   return (G_SHEET_COLUMN_GET_IFACE (column)->get_justification) (column, col);
 }
 
+inline gchar *
+g_sheet_column_get_subtitle (const GSheetColumn *column, gint col)
+{
+  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), NULL);
+
+  if  ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_subtitle)
+    return NULL;
+
+  return (G_SHEET_COLUMN_GET_IFACE (column)->get_subtitle) (column, col);
+}
 
 
 
index ac2e29625f57a2965a01e26a434923c30a3e7470..6526cdffbfa7a589e46205e0a74211e5e285fb29 100644 (file)
@@ -72,6 +72,8 @@ struct _GSheetColumnIface
 
   GtkStateType  (*get_button_state)(const GSheetColumn *geo, gint col);
   gchar * (*get_button_label)(const GSheetColumn *geo, gint col);
+  gchar * (*get_subtitle)(const GSheetColumn *geo, gint col);
+
   gboolean      (*get_button_visibility)(const GSheetColumn *geo,
                                        gint col);
   const GtkSheetChild * (*get_button_child)(const GSheetColumn *geo,
@@ -102,6 +104,8 @@ inline gboolean  g_sheet_column_get_sensitivity(const GSheetColumn *gcolumn,
 inline GtkSheetButton *g_sheet_column_get_button(const GSheetColumn *gcolumn,
                                             gint col);
 
+gchar *g_sheet_column_get_subtitle (const GSheetColumn *, gint);
+
 inline GtkJustification g_sheet_column_get_justification(const GSheetColumn *gcolumn, gint col);
 
 
index ba5d338367031ed5ec3708685dbee6d4cec02f58..bc3652235e7587aede51689a349b8460aaa7d6da 100644 (file)
@@ -172,6 +172,19 @@ g_sheet_row_get_button(const GSheetRow *row_geo,
   return button;
 }
 
+inline gchar *
+g_sheet_row_get_subtitle (const GSheetRow *row_geo, gint row)
+{
+  g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), NULL);
+
+  if ( ! G_SHEET_ROW_GET_IFACE (row_geo)->get_subtitle )
+    return NULL;
+
+  return (G_SHEET_ROW_GET_IFACE (row_geo)->get_subtitle) (row_geo, row);
+}
+
+
+
 
 gint
 g_sheet_row_get_row_count(const GSheetRow *geo, gpointer data)
index b96cccd778d66a95bf775287344483622fd19338..3917b0319a8cac52edaff5ff77ea929666c737c4 100644 (file)
@@ -72,6 +72,8 @@ struct _GSheetRowIface
   gchar * (*get_button_label)(const GSheetRow *geo, gint row,
                              gpointer);
 
+  gchar * (*get_subtitle) (const GSheetRow *geo, gint row);
+
   gboolean      (*get_button_visibility)(const GSheetRow *geo,
                                        gint row, gpointer);
 
@@ -120,6 +122,9 @@ void g_sheet_row_rows_deleted(GSheetRow *geo,
                                      gint first, gint n_rows);
 
 
+gchar *g_sheet_row_get_subtitle (const GSheetRow *row_geo, gint row);
+
+
 G_END_DECLS
 
 #endif /* __G_SHEET_ROW_IFACE_H__ */
index 57ae79dfe36d3abb03c3164f5d55df41bb012ec8..ae71057215f37b12054247da15528c4cca5c920a 100644 (file)
@@ -96,6 +96,7 @@ enum
 #define CELL_SPACING 1
 #define DRAG_WIDTH 6
 #define TIMEOUT_FLASH 200
+#define TIMEOUT_HOVER 300
 #define TIME_INTERVAL 8
 #define COLUMN_MIN_WIDTH 10
 #define MINROWS 1
@@ -1167,6 +1168,8 @@ gtk_sheet_init (GtkSheet *sheet)
   gdk_color_parse ("gray", &sheet->grid_color);
   gdk_color_alloc (gdk_colormap_get_system (), &sheet->grid_color);
   sheet->show_grid = TRUE;
+
+  sheet->motion_events = 0;
 }
 
 
@@ -3803,6 +3806,10 @@ gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
   return GTK_STATE_NORMAL;
 }
 
+/* Convert X, Y (in pixels) to *ROW, *COLUMN (in cell coords)
+   -1 indicates the title buttons.
+   If the function returns FALSE, then the results will be unreliable.
+*/
 gboolean
 gtk_sheet_get_pixel_info (GtkSheet *sheet,
                          gint x,
@@ -3810,25 +3817,43 @@ gtk_sheet_get_pixel_info (GtkSheet *sheet,
                          gint *row,
                          gint *column)
 {
-  gint trow = -1;
-  gint tcol = -1;
+  gint trow, tcol;
+  *row = -G_MAXINT;
+  *column = -G_MAXINT;
 
   g_return_val_if_fail (sheet != NULL, 0);
   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
 
   /* bounds checking, return false if the user clicked
      on a blank area */
-  trow = ROW_FROM_YPIXEL (sheet, y);
-  if (trow >= yyy_row_count (sheet))
+  if (y < 0)
     return FALSE;
 
-  *row = trow;
-
-  tcol = COLUMN_FROM_XPIXEL (sheet, x);
-  if (tcol >= xxx_column_count (sheet))
+  if (x < 0)
     return FALSE;
 
-  *column = tcol;
+  if ( y < sheet->column_title_area.height + sheet->column_title_area.y)
+    *row = -1;
+
+  else
+    {
+      trow = ROW_FROM_YPIXEL (sheet, y);
+      if (trow > yyy_row_count (sheet))
+       return FALSE;
+
+      *row = trow;
+    }
+
+  if ( x < sheet->row_title_area.width + sheet->row_title_area.x)
+    *column = -1;
+  else
+    {
+      tcol = COLUMN_FROM_XPIXEL (sheet, x);
+      if (tcol > xxx_column_count (sheet))
+       return FALSE;
+
+      *column = tcol;
+    }
 
   return TRUE;
 }
@@ -5271,6 +5296,159 @@ gtk_sheet_button_release (GtkWidget * widget,
   return TRUE;
 }
 
+/* Shamelessly lifted from gtktooltips */
+static gboolean
+gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
+{
+  GtkRequisition req;
+
+  gtk_widget_size_request (tip_window, &req);
+  gtk_paint_flat_box (tip_window->style, tip_window->window,
+                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+                     NULL, GTK_WIDGET(tip_window), "tooltip",
+                     0, 0, req.width, req.height);
+
+  return FALSE;
+}
+
+static GtkSheetHoverTitle *
+create_hover_window (void)
+{
+  GtkSheetHoverTitle *hw = malloc (sizeof (*hw));
+
+  hw->window = gtk_window_new (GTK_WINDOW_POPUP);
+
+  gtk_window_set_type_hint (GTK_WINDOW (hw->window),
+                           GDK_WINDOW_TYPE_HINT_TOOLTIP);
+
+  gtk_widget_set_app_paintable (hw->window, TRUE);
+  gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
+  gtk_widget_set_name (hw->window, "gtk-tooltips");
+  gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
+
+  g_signal_connect (hw->window,
+                   "expose_event",
+                   G_CALLBACK (gtk_sheet_subtitle_paint_window),
+                   NULL);
+
+  hw->label = gtk_label_new (NULL);
+
+
+  gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
+  gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
+
+  gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
+
+  gtk_widget_show (hw->label);
+
+  g_signal_connect (hw->window,
+                   "destroy",
+                   G_CALLBACK (gtk_widget_destroyed),
+                   &hw->window);
+
+  return hw;
+}
+
+#define HOVER_WINDOW_Y_OFFSET 2
+
+static void
+show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
+{
+  gint x, y;
+  gint px, py;
+  gint width;
+
+  if ( ! subtitle )
+    return;
+
+  if ( ! sheet->hover_window)
+    {
+      sheet->hover_window = create_hover_window ();
+      gtk_widget_add_events (GTK_WIDGET (sheet), GDK_LEAVE_NOTIFY_MASK);
+
+      g_signal_connect_swapped (sheet, "leave-notify-event",
+                               G_CALLBACK (gtk_widget_hide),
+                               sheet->hover_window->window);
+    }
+
+  gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
+                     subtitle);
+
+
+  sheet->hover_window->row = row;
+  sheet->hover_window->column = column;
+
+  gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
+
+  gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
+
+  gtk_widget_show (sheet->hover_window->window);
+
+  width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
+
+  if (row == -1 )
+    {
+      x += px;
+      x -= width / 2;
+      y += sheet->column_title_area.y;
+      y += sheet->column_title_area.height;
+      y += HOVER_WINDOW_Y_OFFSET;
+    }
+
+  if ( column == -1 )
+    {
+      y += py;
+      x += sheet->row_title_area.x;
+      x += sheet->row_title_area.width * 2 / 3.0;
+    }
+
+  gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
+                  x, y);
+}
+
+static gboolean
+motion_timeout_callback (gpointer data)
+{
+  GtkSheet *sheet = GTK_SHEET (data);
+  if ( --sheet->motion_events == 0 )
+    {
+      gint x, y;
+      gint row, column;
+      gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
+
+      if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
+       {
+         if ( column == -1 && row == -1 )
+           return FALSE;
+
+         if ( column == -1)
+           {
+             GSheetRow *row_geo = sheet->row_geometry;
+             gchar *text;
+
+             text = g_sheet_row_get_subtitle (row_geo, row);
+
+             show_subtitle (sheet, row, column, text);
+             g_free (text);
+           }
+
+         if ( row == -1)
+           {
+             GSheetColumn *col_geo = sheet->column_geometry;
+             gchar *text;
+
+             text = g_sheet_column_get_subtitle (col_geo, column);
+
+             show_subtitle (sheet, row, column, text );
+
+             g_free (text);
+           }
+       }
+    }
+
+  return FALSE;
+}
+
 static gint
 gtk_sheet_motion (GtkWidget * widget,
                  GdkEventMotion * event)
@@ -5291,6 +5469,26 @@ gtk_sheet_motion (GtkWidget * widget,
   x = event->x;
   y = event->y;
 
+  if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window))
+    {
+      sheet->motion_events++;
+      g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
+    }
+  else
+    {
+      gint row, column;
+      gint wx, wy;
+      gtk_widget_get_pointer (widget, &wx, &wy);
+
+      if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
+       {
+         if ( row != sheet->hover_window->row || column != sheet->hover_window->column)
+           {
+             gtk_widget_hide (sheet->hover_window->window);
+           }
+       }
+    }
+
   if (event->window == sheet->column_title_window &&
       gtk_sheet_columns_resizable (sheet))
     {
@@ -5467,7 +5665,8 @@ gtk_sheet_motion (GtkWidget * widget,
 
       /*use half of column width resp. row height as threshold to
        expand selection*/
-      col_threshold = COLUMN_LEFT_XPIXEL (sheet,current_col)+xxx_column_width (sheet,current_col)/2;
+      col_threshold = COLUMN_LEFT_XPIXEL (sheet,current_col) +
+       xxx_column_width (sheet,current_col) / 2;
       if (column > 0)
        {
          if (x < col_threshold)
@@ -5505,7 +5704,6 @@ gtk_sheet_motion (GtkWidget * widget,
       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
          aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
        {
-
          aux = sheet->drag_range;
          sheet->drag_range = sheet->range;
 
@@ -5526,8 +5724,6 @@ gtk_sheet_motion (GtkWidget * widget,
       return TRUE;
     }
 
-
-
   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
 
   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
index f6e420166af1a965c23747ed1c3004c91742e988..da1b0d621bc0757b56c3bd35957830c108727710 100644 (file)
@@ -84,6 +84,7 @@ enum
 typedef struct _GtkSheetClass GtkSheetClass;
 typedef struct _GtkSheetCellAttr     GtkSheetCellAttr;
 typedef struct _GtkSheetCell GtkSheetCell;
+typedef struct _GtkSheetHoverTitle GtkSheetHoverTitle;
 
 
 struct _GtkSheetCellAttr
@@ -103,6 +104,12 @@ struct _GtkSheetCell
   gint col;
 };
 
+struct _GtkSheetHoverTitle
+{
+  GtkWidget *window;
+  GtkWidget *label;
+  gint row, column;
+};
 
 struct _GtkSheet{
   GtkContainer container;
@@ -222,6 +229,10 @@ struct _GtkSheet{
 
   /* clipped range */
   GtkSheetRange clip_range;
+
+  /* Used for the subtitle (popups) */
+  gint motion_events;
+  GtkSheetHoverTitle *hover_window;
 };
 
 struct _GtkSheetClass
index 9635af92d615cba29c60a0216809987c4811aeb1..caa457ae96d2245d1fd7af2199ce8c74264c7409 100644 (file)
@@ -1,3 +1,8 @@
+2007-07-07  John Darrington <john@darrington.wattle.id.au>
+
+       * psppire-data-store.c: Added a tooltip like feature to display
+       the label of variables. 
+       
 2007-07-03  John Darrington <john@darrington.wattle.id.au>
        
        * data-editor.c data-sheet.c: Turned off autoscrolling, and 
index 5bdb8020849498134a58a4400a08184f5a3e4aae..7dca919c019ed11610943029231eedb13c8323cd 100644 (file)
@@ -747,6 +747,27 @@ geometry_get_column_button_label (const GSheetColumn *geom, gint unit)
 }
 
 
+static gchar *
+geometry_get_column_subtitle (const GSheetColumn *geom, gint unit)
+{
+  gchar *text;
+  const struct variable *v ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+
+  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+    return NULL;
+
+  v = psppire_dict_get_variable (ds->dict, unit);
+
+  if ( ! var_has_label (v))
+    return NULL;
+
+  text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
+
+  return text;
+}
+
+
 static gboolean
 geometry_get_sensitivity (const GSheetColumn *geom, gint unit)
 {
@@ -766,6 +787,7 @@ psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
   iface->get_sensitivity = geometry_get_sensitivity;
   iface->get_justification = geometry_get_justification;
   iface->get_button_label = geometry_get_column_button_label;
+  iface->get_subtitle = geometry_get_column_subtitle;
 }