#define CELL_SPACING 1
#define DRAG_WIDTH 6
-#define TIMEOUT_SCROLL 20
#define TIMEOUT_FLASH 200
+#define TIMEOUT_HOVER 300
#define TIME_INTERVAL 8
#define COLUMN_MIN_WIDTH 10
#define MINROWS 1
gint row, gint column);
/* Clipped Range */
-static gint gtk_sheet_scroll (gpointer data);
static gint gtk_sheet_flash (gpointer data);
/* Drawing Routines */
static void gtk_sheet_cell_draw_default (GtkSheet *sheet,
gint row, gint column);
-/* draw cell border */
-static void gtk_sheet_cell_draw_border (GtkSheet *sheet,
- gint row, gint column,
- gint mask);
-
/* draw cell contents */
static void gtk_sheet_cell_draw_label (GtkSheet *sheet,
gint row, gint column);
/* Selection */
-static gint gtk_sheet_move_query (GtkSheet *sheet,
+static gboolean gtk_sheet_move_query (GtkSheet *sheet,
gint row, gint column);
static void gtk_sheet_real_select_range (GtkSheet * sheet,
const GtkSheetRange * range);
sheet->children = NULL;
sheet->flags = 0;
- sheet->selection_mode = GTK_SELECTION_BROWSE;
+ sheet->selection_mode = GTK_SELECTION_NONE;
sheet->freeze_count = 0;
sheet->state = GTK_SHEET_NORMAL;
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;
}
if (sheet->state == GTK_STATE_NORMAL)
if (sheet->sheet_entry && GTK_WIDGET_MAPPED (sheet->sheet_entry))
{
- gtk_sheet_activate_cell (sheet, sheet->active_cell.row, sheet->active_cell.col);
+ gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
+ sheet->active_cell.col);
}
}
if (height < DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))) return;
sheet->column_title_area.height = height;
- sheet->view.row0 = ROW_FROM_YPIXEL (sheet, sheet->column_title_area.height + 1);
+ sheet->view.row0 = ROW_FROM_YPIXEL (sheet,
+ sheet->column_title_area.height + 1);
sheet->view.rowi = ROW_FROM_YPIXEL (sheet, sheet->sheet_window_height - 1);
adjust_scrollbars (sheet);
return sheet->row_titles_visible;
}
-
-
void
-gtk_sheet_moveto (GtkSheet * sheet,
+gtk_sheet_moveto (GtkSheet *sheet,
gint row,
gint column,
gfloat row_align,
width = sheet->sheet_window_width;
/* adjust vertical scrollbar */
-
if (row >= 0 && row_align >= 0.)
{
y = ROW_TOP_YPIXEL (sheet, row) - sheet->voffset
- - (gint) ( row_align*height + (1. - row_align) * yyy_row_height (sheet, row));
+ - (gint) ( row_align*height + (1. - row_align)
+ * yyy_row_height (sheet, row));
/* This forces the sheet to scroll when you don't see the entire cell */
min_row = row;
if (!GTK_SHEET_IN_CLIP (sheet)) return;
GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_CLIP);
+
gtk_timeout_remove (sheet->clip_timer);
gtk_sheet_range_draw (sheet, &sheet->clip_range);
GDK_THREADS_LEAVE ();
return TRUE;
-
}
static void
void
gtk_sheet_get_visible_range (GtkSheet *sheet, GtkSheetRange *range)
{
-
g_return_if_fail (sheet != NULL);
g_return_if_fail (GTK_IS_SHEET (sheet)) ;
g_return_if_fail (range != NULL);
range->col0 = MIN_VISIBLE_COLUMN (sheet);
range->rowi = MAX_VISIBLE_ROW (sheet);
range->coli = MAX_VISIBLE_COLUMN (sheet);
-
}
GtkAdjustment *
sheet->button = NULL;
}
- if (sheet->timer)
- {
- gtk_timeout_remove (sheet->timer);
- sheet->timer = 0;
- }
-
if (sheet->clip_timer)
{
gtk_timeout_remove (sheet->clip_timer);
}
}
-static void
-gtk_sheet_cell_draw_border (GtkSheet *sheet, gint row, gint col, gint mask)
-{
- GtkWidget *widget;
- GdkGC *fg_gc, *bg_gc;
- GtkSheetCellAttr attributes;
- GdkRectangle area;
- guint width;
-
- g_return_if_fail (sheet != NULL);
-
- /* bail now if we arn't drawable yet */
- if (!GTK_WIDGET_DRAWABLE (sheet)) return;
-
- if (row < 0 || row >= yyy_row_count (sheet)) return;
- if (col < 0 || col >= xxx_column_count (sheet)) return;
- if (!xxx_column_is_visible (sheet, col)) return;
- if (!yyy_row_is_visible (sheet, row)) return;
-
- widget = GTK_WIDGET (sheet);
-
- gtk_sheet_get_attributes (sheet, row, col, &attributes);
-
- /* select GC for background rectangle */
- gdk_gc_set_foreground (sheet->fg_gc, &attributes.border.color);
- gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
-
- fg_gc = sheet->fg_gc;
- bg_gc = sheet->bg_gc;
-
- area.x = COLUMN_LEFT_XPIXEL (sheet,col);
- area.y = ROW_TOP_YPIXEL (sheet,row);
- area.width = xxx_column_width (sheet, col);
- area.height = yyy_row_height (sheet, row);
-
- width = attributes.border.width;
- gdk_gc_set_line_attributes (sheet->fg_gc, attributes.border.width,
- attributes.border.line_style,
- attributes.border.cap_style,
- attributes.border.join_style);
- if (width > 0)
- {
-
- if (attributes.border.mask & GTK_SHEET_LEFT_BORDER & mask)
- gdk_draw_line (sheet->pixmap, sheet->fg_gc,
- area.x, area.y - width / 2,
- area.x, area.y + area.height + width / 2+1);
-
- if (attributes.border.mask & GTK_SHEET_RIGHT_BORDER & mask)
- gdk_draw_line (sheet->pixmap, sheet->fg_gc,
- area.x + area.width, area.y - width / 2,
- area.x + area.width,
- area.y + area.height + width / 2+1);
-
- if (attributes.border.mask & GTK_SHEET_TOP_BORDER & mask)
- gdk_draw_line (sheet->pixmap, sheet->fg_gc,
- area.x - width / 2,area.y,
- area.x + area.width + width / 2+1,
- area.y);
-
- if (attributes.border.mask & GTK_SHEET_BOTTOM_BORDER & mask)
- gdk_draw_line (sheet->pixmap, sheet->fg_gc,
- area.x - width / 2, area.y + area.height,
- area.x + area.width + width / 2+1,
- area.y + area.height);
- }
-
-}
-
-
static void
gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col)
{
}
-
-
static void
gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
{
GTK_WIDGET (sheet)->style->white_gc,
TRUE,
0,0,
- sheet->sheet_window_width,sheet->sheet_window_height);
-
+ sheet->sheet_window_width,
+ sheet->sheet_window_height);
}
else
{
for (j = drawing_range.col0; j <= drawing_range.coli; j++)
{
gtk_sheet_cell_draw_default (sheet, i, j);
+ gtk_sheet_cell_draw_label (sheet, i, j);
}
- for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
- for (j = drawing_range.col0; j <= drawing_range.coli; j++)
- {
- gtk_sheet_cell_draw_border (sheet, i - 1, j, GTK_SHEET_BOTTOM_BORDER);
- gtk_sheet_cell_draw_border (sheet, i + 1, j, GTK_SHEET_TOP_BORDER);
- gtk_sheet_cell_draw_border (sheet, i, j - 1, GTK_SHEET_RIGHT_BORDER);
- gtk_sheet_cell_draw_border (sheet, i, j + 1, GTK_SHEET_LEFT_BORDER);
- gtk_sheet_cell_draw_border (sheet, i, j, 15);
- }
-
- for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
- for (j = drawing_range.col0; j <= drawing_range.coli; j++)
- gtk_sheet_cell_draw_label (sheet, i, j);
-
- for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
- for (j= xxx_column_left_column (sheet, drawing_range.col0);
- j < drawing_range.col0; j++)
- gtk_sheet_cell_draw_label (sheet, i, j);
-
- for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
- for (j = drawing_range.coli + 1;
- j <= xxx_column_right_column (sheet, drawing_range.coli);
- j++)
- gtk_sheet_cell_draw_label (sheet, i, j);
-
gtk_sheet_draw_backing_pixmap (sheet, drawing_range);
if (sheet->state != GTK_SHEET_NORMAL &&
sheet->active_cell.col >= drawing_range.col0 &&
sheet->active_cell.col <= drawing_range.coli)
gtk_sheet_show_active_cell (sheet);
-
}
static void
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,
+gtk_sheet_get_pixel_info (GtkSheet *sheet,
gint x,
gint y,
- gint * row,
- gint * column)
+ gint *row,
+ gint *column)
{
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;
}
sheet->selection_cell.col = col;
GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
+
gtk_sheet_show_active_cell (sheet);
+
g_signal_connect (G_OBJECT (gtk_sheet_get_entry (sheet)),
"changed",
G_CALLBACK (gtk_sheet_entry_changed),
gtk_sheet_size_allocate_entry (sheet);
gtk_widget_map (sheet->sheet_entry);
- gtk_sheet_draw_active_cell (sheet);
gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
GtkWidget *widget;
GdkRectangle area;
gint i;
- gint x,y,width,height;
+ gint x, y, width, height;
widget = GTK_WIDGET (sheet);
x = COLUMN_LEFT_XPIXEL (sheet,new_range.col0);
y = ROW_TOP_YPIXEL (sheet,new_range.row0);
- width = COLUMN_LEFT_XPIXEL (sheet,new_range.coli)- x+
+ width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) - x +
xxx_column_width (sheet, new_range.coli);
- height = ROW_TOP_YPIXEL (sheet,new_range.rowi)- y+
+ height = ROW_TOP_YPIXEL (sheet, new_range.rowi) - y +
yyy_row_height (sheet, new_range.rowi);
area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
gdk_draw_rectangle (sheet->sheet_window,
sheet->xor_gc,
FALSE,
- x + i,y + i,
- width - 2 * i,height - 2 * i);
+ x + i,
+ y + i,
+ width - 2 * i,
+ height - 2 * i);
gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
- gtk_sheet_draw_corners (sheet, new_range);
+ gtk_sheet_draw_corners (sheet, new_range);
}
static void
g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
+
sheet = GTK_SHEET (widget);
if (GTK_WIDGET_DRAWABLE (widget))
}
-static gint
+static gboolean
gtk_sheet_button_press (GtkWidget * widget,
GdkEventButton * event)
{
GDK_BUTTON_RELEASE_MASK,
NULL, NULL, event->time);
gtk_grab_add (GTK_WIDGET (sheet));
- sheet->timer = gtk_timeout_add (TIMEOUT_SCROLL, gtk_sheet_scroll, sheet);
+
+ /* This seems to be a kludge to work around a problem where the sheet
+ scrolls to another position. The timeout scrolls it back to its
+ original posn. JMD 3 July 2007
+ */
gtk_widget_grab_focus (GTK_WIDGET (sheet));
if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
+ sheet->selection_mode != GTK_SELECTION_NONE &&
sheet->cursor_drag->type == GDK_SIZING &&
!GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
{
{
gtk_widget_get_pointer (widget, &x, &y);
column = COLUMN_FROM_XPIXEL (sheet, x);
+
if (xxx_column_is_sensitive (sheet, column))
{
gtk_sheet_click_cell (sheet, - 1, column, &veto);
gtk_grab_add (GTK_WIDGET (sheet));
- sheet->timer = gtk_timeout_add (TIMEOUT_SCROLL, gtk_sheet_scroll, sheet);
gtk_widget_grab_focus (GTK_WIDGET (sheet));
GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
}
{
gtk_sheet_click_cell (sheet, row, - 1, &veto);
gtk_grab_add (GTK_WIDGET (sheet));
- sheet->timer = gtk_timeout_add (TIMEOUT_SCROLL, gtk_sheet_scroll, sheet);
gtk_widget_grab_focus (GTK_WIDGET (sheet));
GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
}
return TRUE;
}
+#if 0
static gint
gtk_sheet_scroll (gpointer data)
{
GDK_THREADS_LEAVE ();
return TRUE;
-
}
+#endif
static void
gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto)
if (GTK_SHEET_IN_SELECTION)
gdk_pointer_ungrab (event->time);
- if (sheet->timer)
- gtk_timeout_remove (sheet->timer);
gtk_grab_remove (GTK_WIDGET (sheet));
GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
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)
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))
{
new_cursor = GDK_SIZING;
if ( event->window == sheet->sheet_window &&
+ sheet->selection_mode != GTK_SELECTION_NONE &&
!GTK_SHEET_IN_DRAG (sheet) &&
(POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
GTK_SHEET_IN_RESIZE (sheet)) &&
gdk_window_set_cursor (sheet->sheet_window,sheet->cursor_drag);
}
+
gdk_window_get_pointer (widget->window, &x, &y, &mods);
if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
/*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)
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;
return TRUE;
}
-
-
gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
return TRUE;
}
-static gint
+static gboolean
gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column)
{
gint row_move, column_move;