static SheetObjectView *
gnm_soi_new_view (SheetObject *so, SheetObjectViewContainer *container)
{
	SheetObjectImage *soi = SHEET_OBJECT_IMAGE (so);
	GocItem *item = NULL;
	GdkPixbuf *pixbuf, *placeholder = NULL;

	pixbuf = soi_get_pixbuf (soi, 1.);

	if (pixbuf == NULL) {
		placeholder = gtk_icon_theme_load_icon (
			gtk_icon_theme_get_default (),
			"unknown_image", 100, 0, NULL);
		pixbuf = gdk_pixbuf_copy (placeholder);
	}

	item = goc_item_new (
		gnm_pane_object_group (GNM_PANE (container)),
		so_image_goc_view_get_type (),
		NULL);
	goc_item_hide (goc_item_new (GOC_GROUP (item),
		GOC_TYPE_PIXBUF,
		"pixbuf", pixbuf,
		NULL));
	g_object_unref (G_OBJECT (pixbuf));

	if (placeholder)
		g_object_set_data (G_OBJECT (item), "tile", placeholder);

	return gnm_pane_object_register (so, item, TRUE);
}
Exemple #2
0
static void
ig_reload_style (GnmItemGrid *ig)
{
	GocItem *item = GOC_ITEM (ig);
	GtkStyleContext *context = goc_item_get_style_context (item);
	GtkBorder border;
	GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
	GnmPane *pane = GNM_PANE (item->canvas);

	gtk_style_context_save (context);
	gtk_style_context_add_region (context, "function-marker", 0);
	gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL,
				     &ig->function_marker_color);
	gtk_style_context_get_border_color (context, state,
					    &ig->function_marker_border_color);
	gtk_style_context_restore (context);

	gtk_style_context_save (context);
	gtk_style_context_add_region (context, "pane-divider", 0);
	gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL,
				     &ig->pane_divider_color);
	gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
	ig->pane_divider_width = border.top;  /* Hack? */
	gtk_style_context_restore (context);

	/* ---------------------------------------- */

	context = gtk_widget_get_style_context (GTK_WIDGET (pane));
	gtk_widget_style_get (GTK_WIDGET (pane),
			      "function-indicator-size",
			      &ig->function_marker_size,
			      NULL);
}
static gboolean
comment_view_leave_notify (GocItem *item, double x, double y)
{
	scg_comment_unselect (GNM_PANE (item->canvas)->simple.scg,
	                      GNM_CELL_COMMENT (sheet_object_view_get_so (GNM_SO_VIEW (item))));
	return TRUE;
}
Exemple #4
0
static SheetObjectView *
gnm_soi_new_view (SheetObject *so, SheetObjectViewContainer *container)
{
	SheetObjectImage *soi = SHEET_OBJECT_IMAGE (so);
	GocItem *item = NULL;

	item = goc_item_new (
		gnm_pane_object_group (GNM_PANE (container)),
		so_image_goc_view_get_type (),
		NULL);
	if (soi->image) {
		goc_item_hide (goc_item_new (GOC_GROUP (item),
			GOC_TYPE_IMAGE,
			"image", soi->image,
		        "crop-bottom", soi->crop_bottom,
		        "crop-left", soi->crop_left,
		        "crop-right", soi->crop_right,
		        "crop-top", soi->crop_top,
			NULL));

	} else {
		GdkPixbuf *placeholder = go_gdk_pixbuf_load_from_file
			("res:gnm:pixmaps/unknown_image.png");
		GdkPixbuf *pixbuf = gdk_pixbuf_copy (placeholder);

		goc_item_hide (goc_item_new (GOC_GROUP (item),
			GOC_TYPE_PIXBUF,
			"pixbuf", pixbuf,
			NULL));
		g_object_unref (pixbuf);
		g_object_set_data (G_OBJECT (item), "tile", placeholder);
	}

	return gnm_pane_object_register (so, item, TRUE);
}
Exemple #5
0
static gint
cb_cursor_motion (GnmItemGrid *ig)
{
	Sheet const *sheet = scg_sheet (ig->scg);
	GocCanvas *canvas = ig->canvas_item.canvas;
	GnmPane *pane = GNM_PANE (canvas);
	GdkCursor *cursor;
	GnmCellPos pos;
	GnmHLink *old_link;

	pos.col = gnm_pane_find_col (pane, ig->last_x, NULL);
	pos.row = gnm_pane_find_row (pane, ig->last_y, NULL);

	old_link = ig->cur_link;
	ig->cur_link = sheet_hlink_find (sheet, &pos);
	cursor = (ig->cur_link != NULL) ? ig->cursor_link : ig->cursor_cross;
	if (pane->mouse_cursor != cursor) {
		gnm_pane_mouse_cursor_set (pane, cursor);
		scg_set_display_cursor (ig->scg);
	}

	if (ig->cursor_timer != 0) {
		g_source_remove (ig->cursor_timer);
		ig->cursor_timer = 0;
	}

	if (old_link != ig->cur_link && ig->tip != NULL) {
		gtk_widget_destroy (gtk_widget_get_toplevel (ig->tip));
		ig->tip = NULL;
	}
	return FALSE;
}
Exemple #6
0
static gboolean
item_grid_button_released (GocItem *item, int button, G_GNUC_UNUSED double x_, G_GNUC_UNUSED double y_)
{
	GnmItemGrid *ig = GNM_ITEM_GRID (item);
	GnmPane  *pane = GNM_PANE (item->canvas);
	SheetControlGUI *scg = ig->scg;
	Sheet *sheet = scg_sheet (scg);
	ItemGridSelectionType selecting = ig->selecting;
	GdkEvent *event = goc_canvas_get_cur_event (item->canvas);

	if (button != 1 && button != 2)
		return FALSE;

	gnm_pane_slide_stop (pane);

	switch (selecting) {
	case GNM_ITEM_GRID_NO_SELECTION:
		return TRUE;

	case GNM_ITEM_GRID_SELECTING_FORMULA_RANGE :
/*  Removal of this code (2 lines)                                                */
/*  should fix http://bugzilla.gnome.org/show_bug.cgi?id=63485                    */
/*			sheet_make_cell_visible (sheet,                           */
/*				sheet->edit_pos.col, sheet->edit_pos.row, FALSE); */
		/* Fall through */
	case GNM_ITEM_GRID_SELECTING_CELL_RANGE :
		sv_selection_simplify (scg_view (scg));
		wb_view_selection_desc (
			wb_control_view (scg_wbc (scg)), TRUE, NULL);
		break;

	default:
		g_assert_not_reached ();
	}

	ig->selecting = GNM_ITEM_GRID_NO_SELECTION;
	gnm_simple_canvas_ungrab (item, gdk_event_get_time (event));

	if (selecting == GNM_ITEM_GRID_SELECTING_FORMULA_RANGE)
		gnm_expr_entry_signal_update (
			wbcg_get_entry_logical (scg_wbcg (scg)), TRUE);

	if (selecting == GNM_ITEM_GRID_SELECTING_CELL_RANGE && button == 1) {
		GnmCellPos const *pos = sv_is_singleton_selected (scg_view (scg));
		if (pos != NULL) {
			GnmHLink *link;
			/* check for hyper links */
			link = sheet_hlink_find (sheet, pos);
			if (link != NULL)
				gnm_hlink_activate (link, scg_wbcg (scg));
		}
	}
	return TRUE;
}
static gboolean
comment_view_button2_pressed (GocItem *item, int button, double x, double y)
{
	SheetObject *so;
	GnmRange const *r;
	SheetControlGUI *scg;
	if (button !=1)
		return FALSE;

	scg = GNM_PANE (item->canvas)->simple.scg;
	so = sheet_object_view_get_so (GNM_SO_VIEW (item));
	r = sheet_object_get_range (so);
	dialog_cell_comment (scg->wbcg, so->sheet, &r->start);
	return TRUE;
}
static gboolean
comment_view_enter_notify (GocItem *item, double x, double y)
{
	int ix, iy;
	SheetObject *so;

	gnm_widget_set_cursor_type (GTK_WIDGET (item->canvas), GDK_ARROW);

	gnm_canvas_get_screen_position (item->canvas, x, y, &ix, &iy);
	so = sheet_object_view_get_so (GNM_SO_VIEW (item));

	scg_comment_select (GNM_PANE (item->canvas)->simple.scg,
			    GNM_CELL_COMMENT (so),
			    ix, iy);
	return TRUE;
}
static gboolean
ccombo_activate (GtkTreeView *list, gboolean button)
{
	SheetObjectView		*sov    = g_object_get_data (G_OBJECT (list), SOV_ID);
	GocItem			*view   = GOC_ITEM (sov);
	GnmPane			*pane   = GNM_PANE (view->canvas);
	GnmCComboViewClass	*klass  = GNM_CCOMBO_VIEW_GET_CLASS (sov);

	if ((klass->activate) (sheet_object_view_get_so (sov), list,
			       scg_wbcg (pane->simple.scg), button))
	{
		ccombo_popup_destroy (GTK_WIDGET (list));
		return TRUE;
	}
	return FALSE;
}
static void
comment_view_reload_style (CommentView *cv)
{
	GocItem *item = GOC_ITEM (cv);
	GnmPane *pane = GNM_PANE (item->canvas);
	GtkStyleContext *context;

	context = goc_item_get_style_context (item);
	gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL,
				     &cv->comment_indicator_color);

	context = gtk_widget_get_style_context (GTK_WIDGET (pane));
	gtk_widget_style_get (GTK_WIDGET (pane),
			      "comment-indicator-size",
			      &cv->comment_indicator_size,
			      NULL);
}
static gboolean
comment_view_button_released (GocItem *item, int button, double x, double y)
{
	SheetObject *so;
	int ix, iy;

	if (button != 1)
		return FALSE;

	gnm_canvas_get_screen_position (item->canvas, x, y, &ix, &iy);
	so = sheet_object_view_get_so (GNM_SO_VIEW (item));
	scg_comment_display (GNM_PANE (item->canvas)->simple.scg,
	                     GNM_CELL_COMMENT (so),
			     ix, iy);

	return TRUE;
}
Exemple #12
0
static gint
cb_cursor_come_to_rest (GnmItemGrid *ig)
{
	Sheet const *sheet = scg_sheet (ig->scg);
	GocCanvas *canvas = ig->canvas_item.canvas;
	GnmPane *pane = GNM_PANE (canvas);
	GnmHLink *link;
	gint64 x, y;
	GnmCellPos pos;
	char const *tiptext;

	/* Be anal and look it up in case something has destroyed the link
	 * since the last motion */
	x = ig->last_x;
	y = ig->last_y;
	pos.col = gnm_pane_find_col (pane, x, NULL);
	pos.row = gnm_pane_find_row (pane, y, NULL);

	link = sheet_hlink_find (sheet, &pos);
	if (link != NULL && (tiptext = gnm_hlink_get_tip (link)) != NULL) {
		g_return_val_if_fail (link == ig->cur_link, FALSE);

		if (ig->tip == NULL && strlen (tiptext) > 0) {
			GtkWidget *cw = GTK_WIDGET (canvas);
			int wx, wy;

			gnm_canvas_get_position (canvas, &wx, &wy,
						 ig->last_x, ig->last_y);
			ig->tip = gnumeric_create_tooltip (cw);
			gtk_label_set_text (GTK_LABEL (ig->tip), tiptext);
			/* moving the tip window some pixels from wx,wy in order to
			 * avoid a leave_notify event that would destroy the tip.
			 * see #706659 */
			gtk_window_move (GTK_WINDOW (gtk_widget_get_toplevel (ig->tip)),
					 wx + 10, wy + 10);
			gtk_widget_show_all (gtk_widget_get_toplevel (ig->tip));
		}
	}

	ig->tip_timer = 0;
	return FALSE;
}
Exemple #13
0
/**
 * gnm_cell_combo_view_new:
 * @so: #SheetObject
 * @type: #GType
 * @container: SheetObjectViewContainer (a GnmPane)
 *
 * Create and register an in cell combo to pick from an autofilter list.
 **/
SheetObjectView *
gnm_cell_combo_view_new (SheetObject *so, GType type,
			     SheetObjectViewContainer *container)
{
	GnmPane *pane = GNM_PANE (container);
	GtkWidget *view_widget = gtk_button_new ();
	GocItem *ccombo = goc_item_new (pane->object_views, type, NULL);
	goc_item_new (GOC_GROUP (ccombo), GOC_TYPE_WIDGET,
		"widget",	view_widget,
		NULL);
	gtk_widget_set_can_focus (view_widget, FALSE);

	gtk_container_add (GTK_CONTAINER (view_widget),
		ccombo_create_arrow (GNM_CCOMBO_VIEW (ccombo), so));
	g_signal_connect_swapped (view_widget, "pressed",
		G_CALLBACK (cb_ccombo_button_pressed), ccombo);
	gtk_widget_show_all (view_widget);

	return gnm_pane_object_register (so, ccombo, FALSE);
}
Exemple #14
0
static gboolean
ig_obj_create_begin (GnmItemGrid *ig, int button, gint64 x, gint64 y)
{
	GnmPane *pane = GNM_PANE (GOC_ITEM (ig)->canvas);
	SheetObject *so = ig->scg->wbcg->new_object;
	SheetObjectAnchor anchor;
	double coords[4];

	g_return_val_if_fail (ig->scg->selected_objects == NULL, TRUE);
	g_return_val_if_fail (so != NULL, TRUE);

	coords[0] = coords[2] = x;
	coords[1] = coords[3] = y;
	sheet_object_anchor_init (&anchor, NULL, NULL, GOD_ANCHOR_DIR_DOWN_RIGHT);
	scg_object_coords_to_anchor (ig->scg, coords, &anchor);
	sheet_object_set_anchor (so, &anchor);
	sheet_object_set_sheet (so, scg_sheet (ig->scg));
	scg_object_select (ig->scg, so);
	gnm_pane_object_start_resize (pane, button, x, y, so, 7, TRUE);

	return TRUE;
}
Exemple #15
0
static SheetObjectView *
gnm_so_path_new_view (SheetObject *so, SheetObjectViewContainer *container)
{
	GnmSOPath *sop = GNM_SO_PATH (so);
	GnmSOPathView *item;
	/* FIXME: this is unsafe if the paths change after the view is created,
	 * but this can't occur for now */
	unsigned i;

	if (sop->path == NULL && sop->paths == NULL)
		return NULL;
	item = (GnmSOPathView *) goc_item_new (
	                            gnm_pane_object_group (GNM_PANE (container)),
	                            so_path_goc_view_get_type (),
	                            NULL);
	if (sop->path)
		item->path = goc_item_new (GOC_GROUP (item),
		                           GOC_TYPE_PATH,
		                           "closed", TRUE,
		                           "fill-rule", TRUE,
		                           NULL);
	else {
		item->paths = g_ptr_array_sized_new (sop->paths->len);
		g_ptr_array_set_free_func (item->paths, g_object_unref);
		for (i = 0; i < sop->paths->len; i++)
			g_ptr_array_add (item->paths,
				             goc_item_new (GOC_GROUP (item),
				                           GOC_TYPE_PATH,
				                           "closed", TRUE,
				                           "fill-rule", TRUE,
				                           NULL));
	}
	cb_gnm_so_path_changed (sop, NULL, item);
	g_signal_connect_object (sop,
		"notify::style", G_CALLBACK (cb_gnm_so_path_changed),
		item, 0);
	return gnm_pane_object_register (so, GOC_ITEM (item), TRUE);
}
Exemple #16
0
static gboolean
item_grid_motion (GocItem *item, double x_, double y_)
{
	GnmItemGrid *ig = GNM_ITEM_GRID (item);
	GocCanvas *canvas = item->canvas;
	GnmPane   *pane = GNM_PANE (canvas);
	GnmPaneSlideHandler slide_handler = NULL;
	gint64 x = x_ * canvas->pixels_per_unit, y = y_ * canvas->pixels_per_unit;
	switch (ig->selecting) {
	case GNM_ITEM_GRID_NO_SELECTION:
		if (ig->cursor_timer == 0)
			ig->cursor_timer = g_timeout_add (100,
				(GSourceFunc)cb_cursor_motion, ig);
		if (ig->tip_timer != 0)
			g_source_remove (ig->tip_timer);
		ig->tip_timer = g_timeout_add (500,
				(GSourceFunc)cb_cursor_come_to_rest, ig);
		ig->last_x = x;
		ig->last_y = y;
		return TRUE;
	case GNM_ITEM_GRID_SELECTING_CELL_RANGE :
		slide_handler = &cb_extend_cell_range;
		break;
	case GNM_ITEM_GRID_SELECTING_FORMULA_RANGE :
		slide_handler = &cb_extend_expr_range;
		break;
	default:
		g_assert_not_reached ();
	}

	gnm_pane_handle_motion (pane, canvas, x, y,
		GNM_PANE_SLIDE_X | GNM_PANE_SLIDE_Y |
		GNM_PANE_SLIDE_AT_COLROW_BOUND,
		slide_handler, NULL);
	return TRUE;
}
Exemple #17
0
/**
 * gnm_cell_combo_view_popdown:
 * @sov: #SheetObjectView
 * @activate_time: event time
 *
 * Open the popup window associated with @sov
 **/
void
gnm_cell_combo_view_popdown (SheetObjectView *sov, guint32 activate_time)
{
	GocItem		   *view   = GOC_ITEM (sov);
	GnmPane		   *pane   = GNM_PANE (view->canvas);
	SheetControlGUI	   *scg    = pane->simple.scg;
	SheetObject	   *so     = sheet_object_view_get_so (sov);
	Sheet const	   *sheet  = sheet_object_get_sheet (so);
	GtkWidget *frame,  *popup, *list, *container;
	int root_x, root_y;
	gboolean 	make_buttons = FALSE;
	GtkTreePath	  *clip = NULL, *select = NULL;
	GtkWindow *toplevel = wbcg_toplevel (scg_wbcg (scg));
	GdkWindow *popup_window;
	GdkDevice *device;
	GnmRange const *merge;

	popup = gtk_window_new (GTK_WINDOW_POPUP);

	gtk_window_set_type_hint (GTK_WINDOW (popup), GDK_WINDOW_TYPE_HINT_COMBO);
	gtk_window_group_add_window (gtk_window_get_group (toplevel), GTK_WINDOW (popup));
	go_gtk_window_set_transient (toplevel, GTK_WINDOW (popup));
	gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
	gtk_window_set_decorated (GTK_WINDOW (popup), FALSE);
	gtk_window_set_screen (GTK_WINDOW (popup),
		gtk_widget_get_screen (GTK_WIDGET (toplevel)));

	list = ccombo_create_list (GNM_CCOMBO_VIEW (sov), so, &clip, &select, &make_buttons);

	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
	g_object_set_data (G_OBJECT (list), SOV_ID, sov);

	frame = gtk_frame_new (NULL);
	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);

#if 0
	range_dump (&so->anchor.cell_bound, "");
	g_printerr (" : so = %p, view = %p\n", so, view);
#endif
	if (clip != NULL) {
		GtkWidget *sw = gtk_scrolled_window_new (
			gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (list)),
			gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (list)));
		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
						GTK_POLICY_AUTOMATIC,
						GTK_POLICY_ALWAYS);
		g_object_set_data_full (G_OBJECT (list),
					"clip", clip,
					(GDestroyNotify)gtk_tree_path_free);

		gtk_container_add (GTK_CONTAINER (sw), list);

		/*
		 * Do the sizing in a realize handler as newer versions of
		 * gtk+ give us zero sizes until then.
		 */
		g_signal_connect_after (list, "realize",
					G_CALLBACK (cb_realize_treeview),
					sw);
		container = sw;
	} else
		container = list;

	if (make_buttons) {
		GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
		GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);

		GtkWidget *button;
		button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
		g_signal_connect_swapped (button, "clicked",
			G_CALLBACK (cb_ccombo_cancel_button), list);
		gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 6);
		button = gtk_button_new_from_stock (GTK_STOCK_OK);
		g_signal_connect_swapped (button, "clicked",
			G_CALLBACK (cb_ccombo_ok_button), list);
		gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 6);

		gtk_box_pack_start (GTK_BOX (vbox), container, FALSE, TRUE, 6);
		gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
		container = vbox;
	}

	gtk_container_add (GTK_CONTAINER (frame), container);

	/* do the popup */
	gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (pane)),
			       &root_x, &root_y);
	if (sheet->text_is_rtl) {
		GtkAllocation pa;
		gtk_widget_get_allocation (GTK_WIDGET (pane), &pa);
		root_x += pa.width;
		root_x -= scg_colrow_distance_get (scg, TRUE,
			pane->first.col,
			so->anchor.cell_bound.start.col + 1);
	} else
		root_x += scg_colrow_distance_get (scg, TRUE,
			pane->first.col,
			so->anchor.cell_bound.start.col);
	merge = gnm_sheet_merge_is_corner (sheet, &(so->anchor.cell_bound.start));
	gtk_window_move (GTK_WINDOW (popup), root_x,
		root_y + scg_colrow_distance_get
			 (scg, FALSE,
			  pane->first.row,
			  so->anchor.cell_bound.start.row +
			  ((merge == NULL) ? 1 : range_height (merge))));

	gtk_container_add (GTK_CONTAINER (popup), frame);

	g_signal_connect (popup, "key_press_event",
		G_CALLBACK (cb_ccombo_key_press), list);
	g_signal_connect (popup, "button_press_event",
		G_CALLBACK (cb_ccombo_button_press), list);
	g_signal_connect_after (popup, "button_release_event",
		G_CALLBACK (cb_ccombo_button_release), list);
	g_signal_connect (list, "motion_notify_event",
		G_CALLBACK (cb_ccombo_list_motion), list);
	g_signal_connect (list, "button_press_event",
		G_CALLBACK (cb_ccombo_list_button_press), popup);

	gtk_widget_show_all (popup);

	/* after we show the window setup the selection (showing the list
	 * clears the selection) */
	if (select != NULL) {
		gtk_tree_selection_select_path (
			gtk_tree_view_get_selection (GTK_TREE_VIEW (list)),
			select);
		gtk_tree_view_set_cursor (GTK_TREE_VIEW (list),
			select, NULL, FALSE);
		gtk_tree_path_free (select);
	}

	gtk_widget_grab_focus (popup);
	gtk_widget_grab_focus (GTK_WIDGET (list));
	ccombo_focus_change (GTK_WIDGET (list), TRUE);

	popup_window = gtk_widget_get_window (popup);

	device = gtk_get_current_event_device ();
	if (0 == gdk_device_grab (device, popup_window,
	                          GDK_OWNERSHIP_APPLICATION, TRUE,
	                          GDK_BUTTON_PRESS_MASK |
	                          GDK_BUTTON_RELEASE_MASK |
	                          GDK_POINTER_MOTION_MASK,
	                          NULL, activate_time)) {
		if (0 == gdk_device_grab (gdk_device_get_associated_device (device),
		                          popup_window,
		                          GDK_OWNERSHIP_APPLICATION, TRUE,
		                          GDK_KEY_PRESS_MASK |
		                          GDK_KEY_RELEASE_MASK,
		                          NULL, activate_time))
			gtk_grab_add (popup);
		else
			gdk_device_ungrab (device, activate_time);
	}
}
Exemple #18
0
static gboolean
item_grid_draw_region (GocItem const *item, cairo_t *cr,
		       double x_0, double y_0, double x_1, double y_1)
{
	GocCanvas *canvas = item->canvas;
	double scale = canvas->pixels_per_unit;
	gint64 x0 = x_0 * scale, y0 = y_0 * scale, x1 = x_1 * scale, y1 = y_1 * scale;
	gint width  = x1 - x0;
	gint height = y1 - y0;
	GnmPane *pane = GNM_PANE (canvas);
	Sheet const *sheet = scg_sheet (pane->simple.scg);
	WBCGtk *wbcg = scg_wbcg (pane->simple.scg);
	GnmCell const * const edit_cell = wbcg->editing_cell;
	GnmItemGrid *ig = GNM_ITEM_GRID (item);
	ColRowInfo const *ri = NULL, *next_ri = NULL;
	int const dir = sheet->text_is_rtl ? -1 : 1;
	SheetView const *sv = scg_view (ig->scg);
	WorkbookView *wbv = sv_wbv (sv);
	gboolean show_function_cell_markers = wbv->show_function_cell_markers;
	gboolean show_extension_markers = wbv->show_extension_markers;
	 /* we use the selected background color from an entry for selected cells */
	GtkWidget *entry = gtk_entry_new ();
	GtkStyleContext *ctxt = gtk_widget_get_style_context (entry);

	/* To ensure that far and near borders get drawn we pretend to draw +-2
	 * pixels around the target area which would include the surrounding
	 * borders if necessary */
	/* TODO : there is an opportunity to speed up the redraw loop by only
	 * painting the borders of the edges and not the content.
	 * However, that feels like more hassle that it is worth.  Look into this someday.
	 */
	int x;
	gint64 y, start_x, offset;
	int col, row, n, start_col, end_col;
	int start_row = gnm_pane_find_row (pane, y0-2, &y);
	int end_row = gnm_pane_find_row (pane, y1+2, NULL);
	gint64 const start_y = y - canvas->scroll_y1 * scale;

	GnmStyleRow sr, next_sr;
	GnmStyle const **styles;
	GnmBorder const **borders, **prev_vert;
	GnmBorder const *none =
		sheet->hide_grid ? NULL : gnm_style_border_none ();

	GnmRange     view;
	GSList	 *merged_active, *merged_active_seen,
		 *merged_used, *merged_unused, *ptr, **lag;

	int *colwidths = NULL;

	gboolean const draw_selection =
		ig->scg->selected_objects == NULL &&
		wbcg->new_object == NULL;

	start_col = gnm_pane_find_col (pane, x0-2, &start_x);
	end_col   = gnm_pane_find_col (pane, x1+2, NULL);

	g_return_val_if_fail (start_col <= end_col, TRUE);

#if 0
	g_printerr ("%s:", cell_coord_name (start_col, start_row));
	g_printerr ("%s <= %ld vs ???", cell_coord_name(end_col, end_row), (long)y);
	g_printerr (" [%s]\n", cell_coord_name (ig->bound.end.col, ig->bound.end.row));

#endif

	/* clip to bounds */
	if (end_col > ig->bound.end.col)
		end_col = ig->bound.end.col;
	if (end_row > ig->bound.end.row)
		end_row = ig->bound.end.row;

	/* Skip any hidden cols/rows at the start */
	for (; start_col <= end_col ; ++start_col) {
		ri = sheet_col_get_info (sheet, start_col);
		if (ri->visible)
			break;
	}
	for (; start_row <= end_row ; ++start_row) {
		ri = sheet_row_get_info (sheet, start_row);
		if (ri->visible)
			break;
	}

	/* if everything is hidden no need to draw */
	if (end_col < ig->bound.start.col || start_col > ig->bound.end.col ||
	    end_row < ig->bound.start.row || start_row > ig->bound.end.row)
		return TRUE;

	/* Respan all rows that need it.  */
	for (row = start_row; row <= end_row; row++) {
		ColRowInfo const *ri = sheet_row_get_info (sheet, row);
		if (ri->visible && ri->needs_respan)
			row_calc_spans ((ColRowInfo *)ri, row, sheet);
	}

	sheet_style_update_grid_color (sheet);

	/* Fill entire region with default background (even past far edge) */
	cairo_save (cr);
	if (canvas->direction == GOC_DIRECTION_LTR)
		gtk_render_background (goc_item_get_style_context (item),
				       cr,
				       x0 - canvas->scroll_x1 * scale,
				       y0 - canvas->scroll_y1 * scale,
				       width, height);
	else
		gtk_render_background (goc_item_get_style_context (item),
				       cr,
				       canvas->width - x0 + canvas->scroll_x1 * scale - width,
				       y0 - canvas->scroll_y1 * scale,
				       width, height);
	cairo_restore (cr);

	/* Get ordered list of merged regions */
	merged_active = merged_active_seen = merged_used = NULL;
	merged_unused = gnm_sheet_merge_get_overlap (sheet,
		range_init (&view, start_col, start_row, end_col, end_row));

	/*
	 * allocate a single blob of memory for all 8 arrays of pointers.
	 *	- 6 arrays of n GnmBorder const *
	 *	- 2 arrays of n GnmStyle const *
	 *
	 * then alias the arrays for easy access so that array [col] is valid
	 * for all elements start_col-1 .. end_col+1 inclusive.
	 * Note that this means that in some cases array [-1] is legal.
	 */
	n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */
	style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col,
			g_alloca (n * 8 * sizeof (gpointer)), sheet->hide_grid);

	/* load up the styles for the first row */
	next_sr.row = sr.row = row = start_row;
	sheet_style_get_row (sheet, &sr);

	/* Collect the column widths */
	colwidths = g_alloca (n * sizeof (int));
	colwidths -= start_col;
	for (col = start_col; col <= end_col; col++) {
		ColRowInfo const *ci = sheet_col_get_info (sheet, col);
		colwidths[col] = ci->visible ? ci->size_pixels : -1;
	}

	goc_canvas_c2w (canvas, start_x / scale, 0, &x, NULL);
	start_x = x;
	for (y = start_y; row <= end_row; row = sr.row = next_sr.row, ri = next_ri) {
		/* Restore the set of ranges seen, but still active.
		 * Reinverting list to maintain the original order */
		g_return_val_if_fail (merged_active == NULL, TRUE);

#if DEBUG_SELECTION_PAINT
		g_printerr ("row = %d (startcol = %d)\n", row, start_col);
#endif
		while (merged_active_seen != NULL) {
			GSList *tmp = merged_active_seen->next;
			merged_active_seen->next = merged_active;
			merged_active = merged_active_seen;
			merged_active_seen = tmp;
			MERGE_DEBUG (merged_active->data, " : seen -> active\n");
		}

		/* find the next visible row */
		while (1) {
			++next_sr.row;
			if (next_sr.row <= end_row) {
				next_ri = sheet_row_get_info (sheet, next_sr.row);
				if (next_ri->visible) {
					sheet_style_get_row (sheet, &next_sr);
					break;
				}
			} else {
				for (col = start_col ; col <= end_col; ++col)
					next_sr.vertical [col] =
					next_sr.bottom [col] = none;
				break;
			}
		}

		/* look for merges that start on this row, on the first painted row
		 * also check for merges that start above. */
		view.start.row = row;
		lag = &merged_unused;
		for (ptr = merged_unused; ptr != NULL; ) {
			GnmRange * const r = ptr->data;

			if (r->start.row <= row) {
				GSList *tmp = ptr;
				ptr = *lag = tmp->next;
				if (r->end.row < row) {
					tmp->next = merged_used;
					merged_used = tmp;
					MERGE_DEBUG (r, " : unused -> used\n");
				} else {
					ColRowInfo const *ci =
						sheet_col_get_info (sheet, r->start.col);
					g_slist_free_1 (tmp);
					merged_active = g_slist_insert_sorted (merged_active, r,
								(GCompareFunc)merged_col_cmp);
					MERGE_DEBUG (r, " : unused -> active\n");

					if (ci->visible)
						item_grid_draw_merged_range (cr, ig,
									     start_x, y, &view, r,
									     draw_selection,
									     ctxt);
				}
			} else {
				lag = &(ptr->next);
				ptr = ptr->next;
			}
		}

		for (col = start_col, x = start_x; col <= end_col ; col++) {
			GnmStyle const *style;
			CellSpanInfo const *span;
			ColRowInfo const *ci = sheet_col_get_info (sheet, col);

#if DEBUG_SELECTION_PAINT
			g_printerr ("col [%d] = %d\n", col, x);
#endif
			if (!ci->visible) {
				if (merged_active != NULL) {
					GnmRange const *r = merged_active->data;
					if (r->end.col == col) {
						ptr = merged_active;
						merged_active = merged_active->next;
						if (r->end.row <= row) {
							ptr->next = merged_used;
							merged_used = ptr;
							MERGE_DEBUG (r, " : active2 -> used\n");
						} else {
							ptr->next = merged_active_seen;
							merged_active_seen = ptr;
							MERGE_DEBUG (r, " : active2 -> seen\n");
						}
					}
				}
				continue;
			}

			/* Skip any merged regions */
			if (merged_active != NULL) {
				GnmRange const *r = merged_active->data;
				if (r->start.col <= col) {
					gboolean clear_top, clear_bottom = FALSE;
					int i, first = r->start.col;
					int last  = r->end.col;

					ptr = merged_active;
					merged_active = merged_active->next;
					if (r->end.row <= row) {
						ptr->next = merged_used;
						merged_used = ptr;
						MERGE_DEBUG (r, " : active -> used\n");

						/* in case something managed the bottom of a merge */
						if (r->end.row < row)
							goto plain_draw;
					} else {
						ptr->next = merged_active_seen;
						merged_active_seen = ptr;
						MERGE_DEBUG (r, " : active -> seen\n");
						if (next_sr.row <= r->end.row)
							clear_bottom = TRUE;
					}

					x += dir * scg_colrow_distance_get (
						pane->simple.scg, TRUE, col, last+1);
					col = last;

					if (first < start_col) {
						first = start_col;
						sr.vertical [first] = NULL;
					}
					if (last > end_col) {
						last = end_col;
						sr.vertical [last+1] = NULL;
					}
					clear_top = (r->start.row != row);

					/* Clear the borders */
					for (i = first ; i <= last ; i++) {
						if (clear_top)
							sr.top [i] = NULL;
						if (clear_bottom)
							sr.bottom [i] = NULL;
						if (i > first)
							sr.vertical [i] = NULL;
					}
					continue;
				}
			}

plain_draw : /* a quick hack to deal with 142267 */
			if (dir < 0)
				x -= ci->size_pixels;
			style = sr.styles [col];
			item_grid_draw_background (cr, ig,
				style, col, row, x, y,
				ci->size_pixels, ri->size_pixels,
				draw_selection, ctxt);


			/* Is this part of a span?
			 * 1) There are cells allocated in the row
			 *       (indicated by ri->spans != NULL)
			 * 2) Look in the rows hash table to see if
			 *    there is a span descriptor.
			 */
			if (NULL == ri->spans || NULL == (span = row_span_get (ri, col))) {

				/* If it is being edited pretend it is empty to
				 * avoid problems with long cells'
				 * contents extending past the edge of the edit
				 * box.  Ignore blanks too.
				 */
				GnmCell const *cell = sheet_cell_get (sheet, col, row);
				if (!gnm_cell_is_empty (cell) && cell != edit_cell) {
					if (show_function_cell_markers)
						draw_function_marker (ig, cell, cr, x, y,
								      ci->size_pixels,
								      ri->size_pixels,
								      dir);
					cell_draw (cell, cr,
						   x, y, ci->size_pixels,
						   ri->size_pixels, -1,
						   show_extension_markers);
				}
			/* Only draw spaning cells after all the backgrounds
			 * that we are going to draw have been drawn.  No need
			 * to draw the edit cell, or blanks. */
			} else if (edit_cell != span->cell &&
				   (col == span->right || col == end_col)) {
				GnmCell const *cell = span->cell;
				int const start_span_col = span->left;
				int const end_span_col = span->right;
				int real_x = x;
				ColRowInfo const *cell_col =
					sheet_col_get_info (sheet, cell->pos.col);
				int center_offset = cell_col->size_pixels/2;
				int tmp_width = ci->size_pixels;

				if (col != cell->pos.col)
					style = sheet_style_get (sheet,
						cell->pos.col, row);

				/* x, y are relative to this cell origin, but the cell
				 * might be using columns to the left (if it is set to right
				 * justify or center justify) compute the pixel difference */
				if (dir > 0 && start_span_col != cell->pos.col)
					center_offset += scg_colrow_distance_get (
						pane->simple.scg, TRUE,
						start_span_col, cell->pos.col);
				else if (dir < 0 && end_span_col != cell->pos.col)
					center_offset += scg_colrow_distance_get (
						pane->simple.scg, TRUE,
						cell->pos.col, end_span_col);

				if (start_span_col != col) {
					offset = scg_colrow_distance_get (
						pane->simple.scg, TRUE,
						start_span_col, col);
					tmp_width += offset;
					if (dir > 0)
						real_x -= offset;
					sr.vertical [col] = NULL;
				}
				if (end_span_col != col) {
					offset = scg_colrow_distance_get (
						pane->simple.scg, TRUE,
						col+1, end_span_col + 1);
					tmp_width += offset;
					if (dir < 0)
						real_x -= offset;
				}

				if (show_function_cell_markers)
					draw_function_marker (ig, cell, cr, real_x, y,
							      tmp_width,
							      ri->size_pixels, dir);
				cell_draw (cell, cr,
					   real_x, y, tmp_width,
					   ri->size_pixels, center_offset,
					   show_extension_markers);

			} else if (col != span->left)
				sr.vertical [col] = NULL;

			if (dir > 0)
				x += ci->size_pixels;
		}
		gnm_style_borders_row_draw (prev_vert, &sr,
					cr, start_x, y, y+ri->size_pixels,
					colwidths, TRUE, dir);

		/* In case there were hidden merges that trailed off the end */
		while (merged_active != NULL) {
			GnmRange const *r = merged_active->data;
			ptr = merged_active;
			merged_active = merged_active->next;
			if (r->end.row <= row) {
				ptr->next = merged_used;
				merged_used = ptr;
				MERGE_DEBUG (r, " : active3 -> used\n");
			} else {
				ptr->next = merged_active_seen;
				merged_active_seen = ptr;
				MERGE_DEBUG (r, " : active3 -> seen\n");
			}
		}

		/* roll the pointers */
		borders = prev_vert; prev_vert = sr.vertical;
		sr.vertical = next_sr.vertical; next_sr.vertical = borders;
		borders = sr.top; sr.top = sr.bottom;
		sr.bottom = next_sr.top = next_sr.bottom; next_sr.bottom = borders;
		styles = sr.styles; sr.styles = next_sr.styles; next_sr.styles = styles;

		y += ri->size_pixels;
	}

	if (ig->bound.start.row > 0 && start_y < 1)
		ig_cairo_draw_bound (ig, cr, start_x, 1, x, 1);
	if (ig->bound.start.col > 0) {
		if (canvas->direction == GOC_DIRECTION_RTL && start_x >= goc_canvas_get_width (canvas)) {
			x = goc_canvas_get_width (canvas);
			ig_cairo_draw_bound (ig, cr, x, start_y, x, y);
		} else if (canvas->direction == GOC_DIRECTION_LTR && start_x < 1)
			ig_cairo_draw_bound (ig, cr, 1, start_y, 1, y);
	}

	g_object_ref_sink (entry);
	g_object_unref (entry);

	g_slist_free (merged_used);	   /* merges with bottom in view */
	g_slist_free (merged_active_seen); /* merges with bottom the view */
	g_slist_free (merged_unused);	   /* merges in hidden rows */
	g_return_val_if_fail (merged_active == NULL, TRUE);
	return TRUE;
}
Exemple #19
0
static int
item_grid_button_pressed (GocItem *item, int button, double x_, double y_)
{
	GnmItemGrid *ig = GNM_ITEM_GRID (item);
	GocCanvas    *canvas = item->canvas;
	GnmPane *pane = GNM_PANE (canvas);
	SheetControlGUI *scg = ig->scg;
	WBCGtk *wbcg = scg_wbcg (scg);
	SheetControl	*sc = (SheetControl *)scg;
	SheetView	*sv = sc_view (sc);
	Sheet		*sheet = sv_sheet (sv);
	GnmCellPos	pos;
	gboolean edit_showed_dialog;
	gboolean already_selected;
	GdkEvent *event = goc_canvas_get_cur_event (item->canvas);
	gint64 x = x_ * canvas->pixels_per_unit, y = y_ * canvas->pixels_per_unit;

	gnm_pane_slide_stop (pane);

	pos.col = gnm_pane_find_col (pane, x, NULL);
	pos.row = gnm_pane_find_row (pane, y, NULL);

	/* GnmRange check first */
	if (pos.col >= gnm_sheet_get_max_cols (sheet))
		return TRUE;
	if (pos.row >= gnm_sheet_get_max_rows (sheet))
		return TRUE;

	/* A new object is ready to be realized and inserted */
	if (wbcg->new_object != NULL)
		return ig_obj_create_begin (ig, button, x, y);

	/* If we are not configuring an object then clicking on the sheet
	 * ends the edit.  */
	if (scg->selected_objects == NULL)
		wbcg_focus_cur_scg (wbcg);
	else if (wbc_gtk_get_guru (wbcg) == NULL)
		scg_mode_edit (scg);

	/* If we were already selecting a range of cells for a formula,
	 * reset the location to a new place, or extend the selection.
	 */
	if (button == 1 && scg->rangesel.active) {
		ig->selecting = GNM_ITEM_GRID_SELECTING_FORMULA_RANGE;
		if (event->button.state & GDK_SHIFT_MASK)
			scg_rangesel_extend_to (scg, pos.col, pos.row);
		else
			scg_rangesel_bound (scg, pos.col, pos.row, pos.col, pos.row);
		gnm_pane_slide_init (pane);
		gnm_simple_canvas_grab (item,
			GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
			NULL, gdk_event_get_time (event));
		return TRUE;
	}

	/* If the user is editing a formula (wbcg_rangesel_possible) then we
	 * enable the dynamic cell selection mode.
	 */
	if (button == 1 && wbcg_rangesel_possible (wbcg)) {
		scg_rangesel_start (scg, pos.col, pos.row, pos.col, pos.row);
		ig->selecting = GNM_ITEM_GRID_SELECTING_FORMULA_RANGE;
		gnm_pane_slide_init (pane);
		gnm_simple_canvas_grab (item,
			GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
			NULL, gdk_event_get_time (event));
		return TRUE;
	}

	/* While a guru is up ignore clicks */
	if (wbc_gtk_get_guru (wbcg) != NULL)
		return TRUE;

	/* This was a regular click on a cell on the spreadsheet.  Select it.
	 * but only if the entered expression is valid */
	if (!wbcg_edit_finish (wbcg, WBC_EDIT_ACCEPT, &edit_showed_dialog))
		return TRUE;

	if (button == 1 && !sheet_selection_is_allowed (sheet, &pos))
		return TRUE;

	/* Button == 1 is used to trigger hyperlinks (and possibly similar */
	/* special cases. Otherwise button == 2 should behave exactly like */
	/* button == 1. See bug #700792                                    */
	
	/* buttons 1 and 2 will always change the selection,  the other buttons will
	 * only effect things if the target is not already selected.  */
	already_selected = sv_is_pos_selected (sv, pos.col, pos.row);
	if (button == 1 || button == 2 || !already_selected) {
		if (!(event->button.state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)))
			sv_selection_reset (sv);

		if ((event->button.button != 1 && event->button.button != 2) 
		    || !(event->button.state & GDK_SHIFT_MASK) ||
		    sv->selections == NULL) {
			sv_selection_add_pos (sv, pos.col, pos.row,
					      (already_selected && (event->button.state & GDK_CONTROL_MASK)) ?
					      GNM_SELECTION_MODE_REMOVE :
					      GNM_SELECTION_MODE_ADD);
			sv_make_cell_visible (sv, pos.col, pos.row, FALSE);
		} else sv_selection_extend_to (sv, pos.col, pos.row);
		sheet_update (sheet);
	}

	if (edit_showed_dialog)
		return TRUE;  /* we already ignored the button release */

	switch (button) {
	case 1:
	case 2: {
		guint32 double_click_time;

		/*
		 *  If the second click is on a different cell than the
		 *  first one this cannot be a double-click
		 */
		if (already_selected) {
			g_object_get (gtk_widget_get_settings (GTK_WIDGET (canvas)),
				      "gtk-double-click-time", &double_click_time,
				      NULL);

			if ((ig->last_click_time + double_click_time) > gdk_event_get_time (event) &&
			    wbcg_edit_start (wbcg, FALSE, FALSE)) {
				break;
			}
		}

		ig->last_click_time = gdk_event_get_time (event);
		ig->selecting = GNM_ITEM_GRID_SELECTING_CELL_RANGE;
		gnm_pane_slide_init (pane);
		gnm_simple_canvas_grab (item,
			GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
			NULL, gdk_event_get_time (event));
		break;
	}

	case 3: scg_context_menu (scg, event, FALSE, FALSE);
		break;
	default :
		break;
	}

	return TRUE;
}