Пример #1
0
/*
 * Select the given range and make the it visible.
 */
static gboolean
wb_control_jump (WorkbookControl *wbc, Sheet *sheet, const GnmRangeRef *r)
{
	SheetView *sv;
	GnmCellPos tmp;

	if (r->a.sheet)
		sheet = r->a.sheet;

	if (!sheet_is_visible (sheet)) {
		go_cmd_context_error_invalid
			(GO_CMD_CONTEXT (wbc),
			 _("Cannot jump to an invisible sheet"),
			 sheet->name_unquoted);
		return FALSE;
	}

	sv = sheet_get_view (sheet, wb_control_view (wbc));

	tmp.col = r->a.col;
	tmp.row = r->a.row;
	sv_selection_set (sv, &tmp, r->a.col, r->a.row, r->b.col, r->b.row);
	sv_make_cell_visible (sv, r->b.col, r->b.row, FALSE);
	sv_make_cell_visible (sv, r->a.col, r->a.row, FALSE);
	sv_update (sv);
	if (wb_control_cur_sheet (wbc) != sheet)
		wb_view_sheet_focus (wbc->wb_view, sheet);

	return TRUE;
}
Пример #2
0
void
dialog_workbook_attr (WBCGtk *wbcg)
{
	GtkBuilder   *gui;
	AttrState    *state;

	g_return_if_fail (wbcg != NULL);

	if (gnm_dialog_raise_if_exists (wbcg, WORKBOOK_ATTRIBUTE_KEY))
		return;

	gui = gnm_gtk_builder_load ("res:ui/workbook-attr.ui", NULL, GO_CMD_CONTEXT (wbcg));
        if (gui == NULL)
                return;

	/* Initialize */
	state = g_new (AttrState, 1);
	state->gui = gui;
	state->wbcg = wbcg;
	state->wbv  = wb_control_view (GNM_WBC (wbcg));
	state->wb   = wb_control_get_workbook (GNM_WBC (wbcg));

	attr_dialog_impl (state);

	/* Select the same page the last invocation used */
	attr_dialog_select_page (state, attr_dialog_page);
}
Пример #3
0
void
wb_control_sheet_add (WorkbookControl *wbc, SheetView *sv)
{
	WorkbookControlClass *wbc_class;

	g_return_if_fail (IS_WORKBOOK_CONTROL (wbc));

	wbc_class = WBC_CLASS (wbc);
	if (wbc_class != NULL && wbc_class->sheet.add != NULL) {
		Sheet *new_sheet = sv_sheet (sv);

		wbc_class->sheet.add (wbc, sv);

		/* If this is the current sheet init the display */
		if (new_sheet == wb_control_cur_sheet (wbc)) {
			WorkbookView *wbv = wb_control_view (wbc);
			wb_control_sheet_focus (wbc, new_sheet);
			wb_view_selection_desc (wbv, TRUE, wbc);
			wb_view_edit_line_set (wbv, wbc);
			wb_control_style_feedback (wbc, NULL);
			wb_control_menu_state_update (wbc, MS_ALL);
			wb_control_update_action_sensitivity (wbc);
		}
	}
}
static void
cb_dialog_goto_selection_changed (GtkTreeSelection *the_selection, GotoState *state)
{
	GtkTreeIter  iter;
	GtkTreeModel *model;
	Sheet        *sheet;
	GnmNamedExpr *name;

	if (gtk_tree_selection_get_selected (the_selection, &model, &iter)) {
		gtk_tree_model_get (model, &iter,
				    SHEET_POINTER, &sheet,
				    EXPRESSION, &name,
				    -1);
		if (name && gnm_expr_top_is_rangeref (name->texpr)) {
			GnmParsePos pp;
			char *where_to;

			if (NULL == sheet)
				sheet = wb_control_cur_sheet ( WORKBOOK_CONTROL (state->wbcg));

			parse_pos_init_sheet (&pp, sheet);
			where_to = expr_name_as_string  (name, &pp, gnm_conventions_default);
			if (wb_control_parse_and_jump (WORKBOOK_CONTROL (state->wbcg), where_to))
				gtk_entry_set_text (state->goto_text,
						    where_to);
			g_free (where_to);
			return;
		}
		if (sheet)
			wb_view_sheet_focus (
				wb_control_view (WORKBOOK_CONTROL (state->wbcg)), sheet);
	}
}
Пример #5
0
/*
 * Advanced Filter tool.
 */
gint
advanced_filter (WorkbookControl        *wbc,
		 data_analysis_output_t *dao,
		 GnmValue               *database,
		 GnmValue               *criteria,
		 gboolean               unique_only_flag)
{
        GSList  *crit, *rows;
	GnmEvalPos ep;
	GnmRange r, s;
	SheetView *sv;
	Sheet *sheet = criteria->v_range.cell.a.sheet;

	/* I don't like this -- minimal fix for now.  509427.  */
	if (!VALUE_IS_CELLRANGE (criteria))
		return analysis_tools_invalid_field;

	crit = parse_database_criteria (
		eval_pos_init_sheet (&ep, wb_control_cur_sheet (wbc)),
		database, criteria);

	if (crit == NULL)
		return analysis_tools_invalid_field;

	rows = find_rows_that_match (sheet,
				     database->v_range.cell.a.col,
				     database->v_range.cell.a.row + 1,
				     database->v_range.cell.b.col,
				     database->v_range.cell.b.row,
				     crit, unique_only_flag);

	free_criterias (crit);

	if (rows == NULL)
		return analysis_tools_no_records_found;

	dao_prepare_output (wbc, dao, _("Filtered"));

	filter (dao, sheet, rows,
		database->v_range.cell.a.col,
		database->v_range.cell.b.col, database->v_range.cell.a.row,
		database->v_range.cell.b.row);

	g_slist_free_full (rows, (GDestroyNotify)g_free);

	sv = sheet_get_view (sheet, wb_control_view (wbc));
	s = r = *(selection_first_range (sv, NULL, NULL));
	r.end.row = r.start.row;
	sv_selection_reset (sv);
	sv_selection_add_range (sv, &r);
	sv_selection_add_range (sv, &s);

	wb_control_menu_state_update (wbc, MS_FILTER_STATE_CHANGED);

	return analysis_tools_noerr;
}
Пример #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;
}
Пример #7
0
static void
cb_entry_changed (G_GNUC_UNUSED GtkEntry *entry, WBCGtk *wbcg)
{
	char const *text;
	int text_len;
	WorkbookView *wbv = wb_control_view (GNM_WBC (wbcg));

	text = gtk_entry_get_text (wbcg_get_entry (wbcg));
	text_len = strlen (text);

	if (text_len > wbcg->auto_max_size)
		wbcg->auto_max_size = text_len;

	if (wbv->do_auto_completion && wbcg->auto_completing)
		gnm_complete_start (GNM_COMPLETE (wbcg->auto_complete), text);
}
Пример #8
0
/**
 Create a new Gnumeric workbook, optionally reading a file to
 populate it.
 */
USER_OBJECT_
RGnumeric_newWorkbook(USER_OBJECT_ fileName)
{
 WorkbookControl *wbc;

 USER_OBJECT_ ans;

 if(GET_LENGTH(fileName)) {
  wbc = workbook_control_gui_new(NULL, NULL);
#ifdef HAVE_WORKBOOK_READ
  workbook_read(wbc, CHAR_DEREF(STRING_ELT(fileName,0)));
#else
  wb_view_open (wb_control_view (wbc), wbc, CHAR_DEREF(STRING_ELT(fileName,0)), TRUE);
#endif
 } else {
  wbc = workbook_control_gui_new(NULL, workbook_new_with_sheets(1));
 }

 ans = RGnumeric_workbookReference(wb_control_workbook(wbc));

 return(ans);
}
Пример #9
0
static void
cursor_change (GtkTreeView *tree_view, DialogState *dd)
{
	int matchno;
	int lastmatch = dd->matches->len - 1;
	GtkTreePath *path;

	gtk_tree_view_get_cursor (tree_view, &path, NULL);
	if (path) {
		matchno = gtk_tree_path_get_indices (path)[0];
		gtk_tree_path_free (path);
	} else {
		matchno = -1;
	}

	gtk_widget_set_sensitive (dd->prev_button, matchno > 0);
	gtk_widget_set_sensitive (dd->next_button,
				  matchno >= 0 && matchno < lastmatch);

	if (matchno >= 0 && matchno <= lastmatch) {
		GnmSearchFilterResult *item = g_ptr_array_index (dd->matches, matchno);
		int col = item->ep.eval.col;
		int row = item->ep.eval.row;
		WorkbookControl *wbc = WORKBOOK_CONTROL (dd->wbcg);
		WorkbookView *wbv = wb_control_view (wbc);
		SheetView *sv;

		if (!sheet_is_visible (item->ep.sheet))
			return;

		if (wb_control_cur_sheet (wbc) != item->ep.sheet)
			wb_view_sheet_focus (wbv, item->ep.sheet);
		sv = wb_view_cur_sheet_view (wbv);
		sv_set_edit_pos (sv, &item->ep.eval);
		sv_selection_set (sv, &item->ep.eval, col, row, col, row);
		sv_make_cell_visible (sv, col, row, FALSE);
		sv_update (sv);
	}
}
Пример #10
0
static void
cb_view_ok_clicked (G_GNUC_UNUSED GtkWidget *button,
		    ViewState *state)
{
	WBCGtk *wbcg = state->wbcg;
	WorkbookControl *wbc = WORKBOOK_CONTROL (wbcg);
	WorkbookControl *new_wbc;
	gboolean shared;
	GdkScreen *screen;
	GSList *buttons = gtk_radio_button_get_group (state->location_elsewhere);

	shared = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui, "view_shared")));

	while (buttons)
		if (gtk_toggle_button_get_active (buttons->data))
			break;
		else
			buttons = buttons->next;

	if (!buttons) {
		g_assert_not_reached ();
		return;
	} else if (buttons->data == state->location_elsewhere) {
		const char *name = gtk_entry_get_text (state->location_display_name);
		GdkDisplay *display;
		if (!name)
			return;  /* Just ignore */

		display = gdk_display_open (name);
		if (!display) {
			char *error_str =
				g_strdup_printf (_("Display \"%s\" could not be opened."),
						 name);
			gtk_widget_destroy (state->dialog);
			go_gtk_notice_dialog (wbcg_toplevel (wbcg),
					      GTK_MESSAGE_ERROR,
					      "%s", error_str);
			g_free (error_str);
			return;
		}

		screen = gdk_display_get_default_screen (display);
	} else {
		screen = g_object_get_data (buttons->data, "screen");
	}

	gtk_widget_destroy (state->dialog);

	new_wbc = wb_control_wrapper_new
		(wbc,
		 shared ? wb_control_view (wbc) : NULL,
		 wb_control_get_workbook (wbc),
		 screen);

	if (IS_WBC_GTK (new_wbc)) {
		/* What else would it be?  */
		WBCGtk *new_wbcg = WBC_GTK (new_wbc);
		wbcg_copy_toolbar_visibility (new_wbcg, wbcg);
		_gnm_app_flag_windows_changed ();
	}
}
Пример #11
0
/*
 * Refreshes the buttons on a row (un)selection and selects the chosen sheet
 * for this view.
 */
static void
cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection *ignored,
		      SheetManager *state)
{
	GtkTreeIter  iter;
	Sheet *sheet;
	gboolean has_iter;
	GdkRGBA *fore, *back;
	GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
	GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
	gboolean multiple = gtk_tree_model_iter_n_children(GTK_TREE_MODEL (state->model), NULL) > 1;
	int cnt_sel = g_list_length (selected_rows);
	gboolean single_sel = (cnt_sel < 2);

	gtk_widget_set_sensitive (state->sort_asc_btn, multiple);
	gtk_widget_set_sensitive (state->sort_desc_btn, multiple);

	if (selected_rows == NULL) {
		gtk_widget_set_sensitive (state->up_btn, FALSE);
		gtk_widget_set_sensitive (state->down_btn, FALSE);
		gtk_widget_set_sensitive (state->delete_btn, FALSE);
		gtk_widget_set_sensitive (state->ccombo_back, FALSE);
		gtk_widget_set_sensitive (state->ccombo_fore, FALSE);
		gtk_widget_set_sensitive (state->add_btn, FALSE);
		gtk_widget_set_sensitive (state->duplicate_btn, FALSE);
		return;
	}

	gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model),
				 &iter, (GtkTreePath *) selected_rows->data);

	gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
			    SHEET_POINTER, &sheet,
			    BACKGROUND_COLOUR, &back,
			    FOREGROUND_COLOUR, &fore,
			    -1);
	if (!state->initial_colors_set) {
		go_combo_color_set_color_gdk (GO_COMBO_COLOR (state->ccombo_back), back);
		go_combo_color_set_color_gdk (GO_COMBO_COLOR (state->ccombo_fore), fore);
		state->initial_colors_set = TRUE;
	}
	if (back != NULL)
		gdk_rgba_free (back);
	if (fore != NULL)
		gdk_rgba_free (fore);

	gtk_widget_set_sensitive (state->ccombo_back, TRUE);
	gtk_widget_set_sensitive (state->ccombo_fore, TRUE);
	gtk_widget_set_sensitive (state->delete_btn, sheet_order_cnt_visible (state) > cnt_sel);
	gtk_widget_set_sensitive (state->add_btn, single_sel);
	gtk_widget_set_sensitive (state->duplicate_btn, single_sel);

	has_iter = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->model), &iter);
	g_return_if_fail (has_iter);
	gtk_widget_set_sensitive (state->up_btn,
				  single_sel &&
				  !gtk_tree_selection_iter_is_selected (selection, &iter));
	gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state->model), &iter, NULL,
				       gtk_tree_model_iter_n_children
				       (GTK_TREE_MODEL (state->model), NULL) - 1);
	gtk_widget_set_sensitive (state->down_btn,
				  single_sel &&
				  !gtk_tree_selection_iter_is_selected (selection, &iter));

	if (sheet != NULL)
		wb_view_sheet_focus (
			wb_control_view (GNM_WBC (state->wbcg)), sheet);

	g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
}
Пример #12
0
void
dialog_sheet_order (WBCGtk *wbcg)
{
	SheetManager *state;
	GtkBuilder *gui;
	GtkGrid *grid;
	GOColorGroup *cg;
	Workbook *wb;
	GtkWidget *widget;
	GdkPixbuf *icon;

	g_return_if_fail (wbcg != NULL);

	widget = GTK_WIDGET (wbcg_toplevel (wbcg));

	gui = gnm_gtk_builder_load ("res:ui/sheet-order.ui", NULL, GO_CMD_CONTEXT (wbcg));
        if (gui == NULL)
                return;

	wb = wb_control_get_workbook (GNM_WBC (wbcg));
	if (g_object_get_data (G_OBJECT (wb), SHEET_ORDER_KEY)) {
		GtkWidget *dialog = gtk_message_dialog_new
			(wbcg_toplevel (wbcg),
			 GTK_DIALOG_DESTROY_WITH_PARENT,
			 GTK_MESSAGE_WARNING,
			 GTK_BUTTONS_CLOSE,
			 _("Another view is already managing sheets"));
		go_gtk_dialog_run (GTK_DIALOG (dialog), wbcg_toplevel (wbcg));
		return;
	}
	g_object_set_data (G_OBJECT (wb), SHEET_ORDER_KEY, (gpointer) gui);
	state = g_new0 (SheetManager, 1);
	state->gui = gui;
	state->wbcg = wbcg;
	state->dialog     = go_gtk_builder_get_widget (gui, "sheet-order-dialog");
	state->warning     = go_gtk_builder_get_widget (gui, "warning");
	state->up_btn     = go_gtk_builder_get_widget (gui, "up_button");
	state->down_btn   = go_gtk_builder_get_widget (gui, "down_button");
	state->add_btn   = go_gtk_builder_get_widget (gui, "add_button");
	state->append_btn   = go_gtk_builder_get_widget (gui, "append_button");
	state->duplicate_btn   = go_gtk_builder_get_widget (gui, "duplicate_button");
	state->delete_btn   = go_gtk_builder_get_widget (gui, "delete_button");

	state->apply_names_btn  = go_gtk_builder_get_widget (gui, "ok_button");
	state->sort_asc_btn  = go_gtk_builder_get_widget (gui, "sort-asc-button");
	state->sort_desc_btn  = go_gtk_builder_get_widget (gui, "sort-desc-button");
	state->undo_btn  = go_gtk_builder_get_widget (gui, "undo-button");
	state->cancel_btn  = go_gtk_builder_get_widget (gui, "cancel_button");
	state->advanced_check  = go_gtk_builder_get_widget (gui, "advanced-check");
	state->initial_colors_set = FALSE;
	state->image_padlock = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-protection-yes", GTK_ICON_SIZE_MENU);
	state->image_padlock_no = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-protection-no", GTK_ICON_SIZE_MENU);
	state->image_visible = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-visible", GTK_ICON_SIZE_MENU);
	state->image_ltr = go_gtk_widget_render_icon_pixbuf (widget, "format-text-direction-ltr", GTK_ICON_SIZE_MENU);
	state->image_rtl = go_gtk_widget_render_icon_pixbuf (widget, "format-text-direction-rtl", GTK_ICON_SIZE_MENU);
	/* Listen for changes in the sheet order. */
	state->sheet_order_changed_listener = g_signal_connect (G_OBJECT (wb),
		"sheet_order_changed", G_CALLBACK (cb_sheet_order_changed),
		state);
	state->sheet_added_listener = g_signal_connect (G_OBJECT (wb),
		"sheet_added", G_CALLBACK (cb_sheet_added),
		state);
	state->sheet_deleted_listener = g_signal_connect (G_OBJECT (wb),
		"sheet_deleted", G_CALLBACK (cb_sheet_deleted),
		state);

	grid = GTK_GRID (go_gtk_builder_get_widget (gui,"main-grid"));
	cg = go_color_group_fetch ("back_color_group",
		wb_control_view (GNM_WBC (wbcg)));
	icon = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-bucket", GTK_ICON_SIZE_LARGE_TOOLBAR);
	state->ccombo_back = go_combo_color_new (icon, _("Default"), 0, cg);
	g_object_unref (icon);
	g_object_unref (cg);
	go_combo_color_set_instant_apply (
		GO_COMBO_COLOR (state->ccombo_back), TRUE);
	gtk_grid_attach (grid, state->ccombo_back, 1, 4, 1, 1);
	gtk_widget_set_sensitive (state->ccombo_back, FALSE);

	cg = go_color_group_fetch ("fore_color_group",
		wb_control_view (GNM_WBC (wbcg)));
	icon = go_gtk_widget_render_icon_pixbuf (widget, "font", GTK_ICON_SIZE_LARGE_TOOLBAR);
	state->ccombo_fore = go_combo_color_new (icon, _("Default"), 0, cg);
	g_object_unref (icon);
	g_object_unref (cg);
	go_combo_color_set_instant_apply (
		GO_COMBO_COLOR (state->ccombo_fore), TRUE);
	gtk_grid_attach (grid, state->ccombo_fore, 2, 4, 1, 1);
	gtk_widget_set_sensitive (state->ccombo_fore, FALSE);

	create_sheet_list (state);
	populate_sheet_list (state);

#define CONNECT(o,s,c) g_signal_connect(G_OBJECT(o),s,G_CALLBACK(c),state)
	CONNECT (state->up_btn, "clicked", cb_up);
	CONNECT (state->down_btn, "clicked", cb_down);
	CONNECT (state->sort_asc_btn, "clicked", cb_asc);
	CONNECT (state->sort_desc_btn, "clicked", cb_desc);
	CONNECT (state->add_btn, "clicked", cb_add_clicked);
	CONNECT (state->append_btn, "clicked", cb_append_clicked);
	CONNECT (state->duplicate_btn, "clicked", cb_duplicate_clicked);
	CONNECT (state->delete_btn, "clicked", cb_delete_clicked);
	CONNECT (state->apply_names_btn, "clicked", cb_apply_names_clicked);
	CONNECT (state->cancel_btn, "clicked", cb_cancel_clicked);
	CONNECT (state->undo_btn, "clicked", cb_undo_clicked);
	CONNECT (state->advanced_check, "toggled", cb_adv_check_toggled);
	CONNECT (state->ccombo_back, "color_changed", cb_color_changed_back);
	CONNECT (state->ccombo_fore, "color_changed", cb_color_changed_fore);
	CONNECT (state->model, "rows-reordered", cb_dialog_order_changed);
	state->model_row_insertion_listener =
		CONNECT (state->model, "row-inserted", cb_dialog_order_changed_by_insertion);
#undef CONNECT

	cb_adv_check_toggled (NULL, state);

	gnm_init_help_button (
		go_gtk_builder_get_widget (state->gui, "help_button"),
		GNUMERIC_HELP_LINK_SHEET_MANAGER);

	gtk_widget_set_sensitive (state->undo_btn, wb->undo_commands != NULL);
	gtk_widget_set_sensitive (state->apply_names_btn, FALSE);

	/* a candidate for merging into attach guru */
	wbc_gtk_attach_guru (state->wbcg, GTK_WIDGET (state->dialog));
	g_object_set_data_full (G_OBJECT (state->dialog),
		"state", state, (GDestroyNotify) cb_sheet_order_destroy);
	g_signal_connect (G_OBJECT (state->dialog), "destroy", G_CALLBACK (destroy_cb), NULL);

	gnm_restore_window_geometry (GTK_WINDOW (state->dialog),
					  SHEET_ORDER_KEY);

	go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
				   GTK_WINDOW (state->dialog));
	gtk_widget_show_all (GTK_WIDGET (state->dialog));
}
Пример #13
0
/**
 * wbcg_edit_finish:
 * @wbcg: #WBCGtk
 * @result: what should we do with the content
 * @showed_dialog: If non-NULL will indicate if a dialog was displayed.
 *
 * Return: TRUE if editing completed successfully, or we were no editing.
 **/
gboolean
wbcg_edit_finish (WBCGtk *wbcg, WBCEditResult result,
		  gboolean *showed_dialog)
{
	Sheet *sheet;
	SheetView *sv;
	WorkbookControl *wbc;
	WorkbookView	*wbv;

	g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg), FALSE);

	wbc = GNM_WBC (wbcg);
	wbv = wb_control_view (wbc);

	wbcg_focus_cur_scg (wbcg);

	gnm_expr_entry_close_tips (wbcg_get_entry_logical (wbcg));

	if (showed_dialog != NULL)
		*showed_dialog = FALSE;

	/* Remove the range selection cursor if it exists */
	if (NULL != wbcg->rangesel)
		scg_rangesel_stop (wbcg->rangesel, result == WBC_EDIT_REJECT);

	if (!wbcg_is_editing (wbcg)) {
		/* We may have a guru up even if we are not editing. remove it.
		 * Do NOT remove until later it if we are editing, it is possible
		 * that we may want to continue editing.
		 */
		if (wbcg->edit_line.guru != NULL) {
			GtkWidget *w = wbcg->edit_line.guru;
			wbc_gtk_detach_guru (wbcg);
			gtk_widget_destroy (w);
		}

		return TRUE;
	}

	g_return_val_if_fail (IS_SHEET (wbcg->editing_sheet), TRUE);

	sheet = wbcg->editing_sheet;
	sv = sheet_get_view (sheet, wbv);

	/* Save the results before changing focus */
	if (result != WBC_EDIT_REJECT) {
		ValidationStatus valid = GNM_VALIDATION_STATUS_VALID;
		char *free_txt = NULL;
		char const *txt;
		GnmStyle const *mstyle;
		char const *expr_txt = NULL;
		GOFormat const *fmt;
		GnmValue *value;
		GOUndo *u = NULL;
		GSList	*selection = selection_get_ranges (sv, FALSE);
		GnmParsePos    pp;
		GnmExprTop const *texpr = NULL;

		parse_pos_init_editpos (&pp, sv);

		/* Array only works on single range.  */
		if (result == WBC_EDIT_ACCEPT_ARRAY &&
		    (selection == NULL || selection->next != NULL))
			result = WBC_EDIT_ACCEPT_RANGE;

		/******* Check whether we would split a range ********/

		switch (result) {
		case (WBC_EDIT_ACCEPT_RANGE):
		case (WBC_EDIT_ACCEPT_ARRAY): {
			if (sheet_ranges_split_region (sheet, selection,
						       GO_CMD_CONTEXT (wbc), _("Set Text"))) {
				range_fragment_free (selection);
				if (showed_dialog != NULL)
					*showed_dialog = TRUE;
				return FALSE;
			}

			if (result == WBC_EDIT_ACCEPT_ARRAY &&
			    sheet_range_contains_merges_or_arrays
			    	(sheet, selection->data,
				 GO_CMD_CONTEXT (wbc), _("Set Text"),
				 TRUE, FALSE)) {
				range_fragment_free (selection);
				if (showed_dialog != NULL)
					*showed_dialog = TRUE;
				return FALSE;
			}

			break;
		}
		case (WBC_EDIT_ACCEPT_WO_AC):
		case (WBC_EDIT_ACCEPT): {
			GnmCell const *cell = sheet_cell_get
				(sheet, sv->edit_pos.col, sv->edit_pos.row);
			if (gnm_cell_is_nonsingleton_array (cell)) {
				gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
								    _("Set Text"), NULL);
				if (showed_dialog != NULL)
					*showed_dialog = TRUE;
				range_fragment_free (selection);
				return FALSE;
			}
			break;
		}
		case (WBC_EDIT_REJECT):
		default:
			/* We should not be able to get here! */
			break;
		}


		/******* Check whether the range is locked ********/

		switch (result) {
		case (WBC_EDIT_ACCEPT_RANGE):
		case (WBC_EDIT_ACCEPT_ARRAY): {
			if (cmd_selection_is_locked_effective (sheet, selection, wbc,
							       _("Set Text"))) {
				range_fragment_free (selection);
				if (showed_dialog != NULL)
					*showed_dialog = TRUE;
				return FALSE;
			}
			break;
		}
		case (WBC_EDIT_ACCEPT_WO_AC):
		case (WBC_EDIT_ACCEPT): {
			GnmRange r;
			r.end = r.start = pp.eval;

			if (cmd_cell_range_is_locked_effective (sheet, &r, wbc,
							       _("Set Text"))) {
				range_fragment_free (selection);
				if (showed_dialog != NULL)
					*showed_dialog = TRUE;
				return FALSE;
			}
			break;
		}
		case (WBC_EDIT_REJECT):
		default:
			/* We should not be able to get here! */
			break;
		}
		/*****************************************************/

		txt = wbcg_edit_get_display_text (wbcg);
		mstyle = sheet_style_get (sheet, sv->edit_pos.col, sv->edit_pos.row);
		fmt = gnm_cell_get_format (sheet_cell_fetch (sheet, sv->edit_pos.col,
							     sv->edit_pos.row));

		value = format_match (txt, fmt,
				      workbook_date_conv (sheet->workbook));
		if (value == NULL)
			expr_txt = gnm_expr_char_start_p (txt);
		else
			value_release (value);

		/* NOTE : do not modify gnm_expr_char_start_p to exclude "-"
		 * it _can_ start an expression, which is required for rangesel
		 * it just isn't an expression. */
		if (expr_txt != NULL && *expr_txt != '\0' && strcmp (expr_txt, "-")) {
			GnmExprTop const *texpr_test = NULL;
			GnmParseError  perr;

			parse_error_init (&perr);
			texpr_test = gnm_expr_parse_str (expr_txt,
							 &pp, GNM_EXPR_PARSE_DEFAULT, NULL, &perr);
			/* Try adding a single extra closing paren to see if it helps */
			if (texpr_test == NULL && perr.err != NULL &&
			    perr.err->code == PERR_MISSING_PAREN_CLOSE) {
				GnmParseError tmp_err;
				char *tmp = g_strconcat (txt, ")", NULL);
				parse_error_init (&tmp_err);
				texpr_test = gnm_expr_parse_str (gnm_expr_char_start_p (tmp),
								 &pp, GNM_EXPR_PARSE_DEFAULT,
								 NULL, &tmp_err);
				parse_error_free (&tmp_err);

				if (texpr_test != NULL) {
					txt = free_txt = tmp;
					expr_txt = gnm_expr_char_start_p (txt);
				} else
					g_free (tmp);
			}

			if (texpr_test == NULL && perr.err != NULL) {
				ValidationStatus reedit;

				/* set focus _before_ selection.  gtk2 seems to
				 * screw with selection in gtk_entry_grab_focus
				 * (no longer required now that we clear
				 * gtk-entry-select-on-focus) */
				gtk_window_set_focus (wbcg_toplevel (wbcg),
						      (GtkWidget *) wbcg_get_entry (wbcg));

				if (perr.begin_char != 0 || perr.end_char != 0) {
					int offset = expr_txt - txt;
					gtk_editable_select_region (GTK_EDITABLE (wbcg_get_entry (wbcg)),
								    offset + perr.begin_char,
								    offset + perr.end_char);
				} else
					gtk_editable_set_position (
								   GTK_EDITABLE (wbcg_get_entry (wbcg)), -1);

				reedit = wb_control_validation_msg (GNM_WBC (wbcg),
								    GNM_VALIDATION_STYLE_PARSE_ERROR, NULL,
								    perr.err->message);
				if (showed_dialog != NULL)
					*showed_dialog = TRUE;

				parse_error_free (&perr);
				if (reedit == GNM_VALIDATION_STATUS_INVALID_EDIT) {
					range_fragment_free (selection);
					return FALSE;
				}
				/* restore focus to sheet , or we'll leave edit
				 * mode only to jump right back in the new
				 * cell because it looks like someone just
				 * focused on the edit line (eg hit F2) */
				wbcg_focus_cur_scg (wbcg);
			}
			if (texpr_test != NULL)
				gnm_expr_top_unref (texpr_test);
		}

		/* We only enter an array formula if the text is a formula */
		if (result == WBC_EDIT_ACCEPT_ARRAY && !expr_txt)
			result = WBC_EDIT_ACCEPT_RANGE;

		if (result == WBC_EDIT_ACCEPT_ARRAY) {
			GnmParsePos pp_array;
			GnmRange *r = selection->data;

			parse_pos_init (&pp_array, sheet->workbook, sheet, r->start.col, r->start.row);

			if ((texpr = gnm_expr_parse_str
			     (expr_txt, &pp_array, GNM_EXPR_PARSE_DEFAULT,
			      sheet_get_conventions (sheet), NULL)) == NULL)
				result = WBC_EDIT_ACCEPT_RANGE;
		}

		/* We need to save the information that we will temporarily overwrite */
		/* We then assign the information. No need to worry about formatting */
		/* Finally we can check the validation! */

		switch (result) {
		case (WBC_EDIT_ACCEPT_RANGE): {
			GSList	*l;

			for (l = selection; l != NULL; l = l->next) {
				GnmRange *r = l->data;
				u = go_undo_combine (u,  clipboard_copy_range_undo (sheet, r));
			}
			for (l = selection; l != NULL; l = l->next) {
				GnmRange *r = l->data;
				/* We do this separately since there may be overlap between ranges */
				sheet_range_set_text (&pp, r, txt);
				valid =	gnm_validation_eval_range (wbc, sheet, &sv->edit_pos, r,
							       showed_dialog);
				if (valid != GNM_VALIDATION_STATUS_VALID)
					break;
			}
			break;
		}
		case (WBC_EDIT_ACCEPT_ARRAY): {
			GnmRange *r = selection->data;

			u = go_undo_combine (u,  clipboard_copy_range_undo (sheet, r));
			if (texpr) {
				gnm_expr_top_ref (texpr);
				gnm_cell_set_array_formula (sheet,
							    r->start.col, r->start.row,
							    r->end.col, r->end.row,
							    texpr);
				sheet_region_queue_recalc (sheet, r);
			}
			valid =	gnm_validation_eval_range (wbc, sheet, &sv->edit_pos, r,
						       showed_dialog);
			break;
		}
		case (WBC_EDIT_ACCEPT_WO_AC):
		case (WBC_EDIT_ACCEPT): {
			GnmRange r;
			GnmCell *cell;

			range_init_cellpos (&r, &sv->edit_pos);
			u = clipboard_copy_range_undo (sheet, &r);

			cell = sheet_cell_fetch (sheet,
						 sv->edit_pos.col,
						 sv->edit_pos.row);
			sheet_cell_set_text (cell, txt, wbcg->edit_line.markup);
			valid = gnm_validation_eval (wbc, mstyle, sheet, &sv->edit_pos, showed_dialog);
			break;
		}
		case (WBC_EDIT_REJECT):
		default:
			/* We should not be able to get here! */
			break;
		}

		range_fragment_free (selection);

		/* We need to rebuild the original info first. */

		go_undo_undo (u);
		g_object_unref (u);

		/* Now we can respond to our validation information */

		if (valid != GNM_VALIDATION_STATUS_VALID) {
			result = WBC_EDIT_REJECT;
			if (valid == GNM_VALIDATION_STATUS_INVALID_EDIT) {
				gtk_window_set_focus (wbcg_toplevel (wbcg),
					(GtkWidget *) wbcg_get_entry (wbcg));
				g_free (free_txt);
				if (texpr != NULL)
					gnm_expr_top_unref (texpr);
				return FALSE;
			}
		} else {
			if (result == WBC_EDIT_ACCEPT_ARRAY) {
				cmd_area_set_array_expr (wbc, sv, texpr);

			} else {
				PangoAttrList *res_markup = wbcg->edit_line.markup
					? pango_attr_list_copy (wbcg->edit_line.markup)
					: NULL;
				if (result == WBC_EDIT_ACCEPT)
					cmd_set_text (wbc, sheet, &sv->edit_pos, txt, res_markup, TRUE);
				else if (result == WBC_EDIT_ACCEPT_WO_AC)
					cmd_set_text (wbc, sheet, &sv->edit_pos, txt, res_markup, FALSE);
				else
					cmd_area_set_text (wbc, sv, txt, res_markup);
				if (res_markup)
					pango_attr_list_unref (res_markup);
			}
		}
		if (texpr != NULL)
			gnm_expr_top_unref (texpr);
		g_free (free_txt);
	} else {
		if (sv == wb_control_cur_sheet_view (wbc)) {
			/* Redraw the cell contents in case there was a span */
			GnmRange tmp; tmp.start = tmp.end = sv->edit_pos;
			sheet_range_bounding_box (sv->sheet, &tmp);
			sv_redraw_range (wb_control_cur_sheet_view (wbc), &tmp);
		}

		/* Reload the entry widget with the original contents */
		wb_view_edit_line_set (wbv, wbc);
	}

	/* Stop editing */
	wbcg->editing = FALSE;
	wbcg->editing_sheet = NULL;
	wbcg->editing_cell = NULL;

	if (wbcg->edit_line.guru != NULL) {
		GtkWidget *w = wbcg->edit_line.guru;
		wbc_gtk_detach_guru (wbcg);
		gtk_widget_destroy (w);
	}

	if (wbcg->edit_line.signal_insert) {
		g_signal_handler_disconnect (wbcg_get_entry (wbcg),
					     wbcg->edit_line.signal_insert);
		wbcg->edit_line.signal_insert = 0;
	}
	if (wbcg->edit_line.signal_delete) {
		g_signal_handler_disconnect (wbcg_get_entry (wbcg),
					     wbcg->edit_line.signal_delete);
		wbcg->edit_line.signal_delete = 0;
	}
	if (wbcg->edit_line.signal_cursor_pos) {
		g_signal_handler_disconnect (wbcg_get_entry (wbcg),
					     wbcg->edit_line.signal_cursor_pos);
		wbcg->edit_line.signal_cursor_pos = 0;
	}
	if (wbcg->edit_line.signal_selection_bound) {
		g_signal_handler_disconnect (wbcg_get_entry (wbcg),
					     wbcg->edit_line.signal_selection_bound);
		wbcg->edit_line.signal_selection_bound = 0;
	}

	if (wbcg->edit_line.cell_attrs != NULL) {
		pango_attr_list_unref (wbcg->edit_line.cell_attrs);
		wbcg->edit_line.cell_attrs = NULL;
	}

	if (wbcg->edit_line.markup) {
		pango_attr_list_unref (wbcg->edit_line.markup);
		wbcg->edit_line.markup = NULL;
	}

	if (wbcg->edit_line.full_content != NULL) {
		pango_attr_list_unref (wbcg->edit_line.full_content);
		wbcg->edit_line.full_content = NULL;
	}

	if (wbcg->edit_line.cur_fmt) {
		pango_attr_list_unref (wbcg->edit_line.cur_fmt);
		wbcg->edit_line.cur_fmt = NULL;
	}

	/* set pos to 0, to ensure that if we start editing by clicking on the
	 * editline at the last position, we'll get the right style feedback */
	gtk_editable_set_position ((GtkEditable *) wbcg_get_entry (wbcg), 0);

	wb_control_update_action_sensitivity (wbc);

	if (!sheet->workbook->during_destruction) {
		/* restore focus to original sheet in case things were being selected
		 * on a different page.  Do no go through the view, rangesel is
		 * specific to the control.  */
		wb_control_sheet_focus (wbc, sheet);
		/* Only the edit sheet has an edit cursor */
		scg_edit_stop (wbcg_cur_scg (wbcg));
	}
	wbcg_auto_complete_destroy (wbcg);
	wb_control_style_feedback (wbc, NULL);	/* in case markup messed with things */

	return TRUE;
}
Пример #14
0
/**
 * wbcg_edit_start:
 * @wbcg:       The workbook to be edited.
 * @blankp:   If true, erase current cell contents first.  If false, leave the
 *            contents alone.
 * @cursorp:  If true, create an editing cursor in the current sheet.  (If
 *            false, the text will be editing in the edit box above the sheet,
 *            but this is not handled by this function.)
 *
 * Initiate editing of a cell in the sheet.  Note that we have two modes of
 * editing:
 *  1) in-cell editing when you just start typing, and
 *  2) above sheet editing when you hit F2.
 *
 * Returns TRUE if we did indeed start editing.  Returns FALSE if the
 * cell-to-be-edited was locked.
 */
gboolean
wbcg_edit_start (WBCGtk *wbcg,
		 gboolean blankp, gboolean cursorp)
{
	/* We could save this, but the situation is rare, if confusing.  */
	static gboolean warn_on_text_format = TRUE;
	SheetView *sv;
	SheetControlGUI *scg;
	GnmCell *cell;
	char *text = NULL;
	int col, row;
	WorkbookView *wbv;
	int cursor_pos = -1;

	g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg), FALSE);

	if (wbcg_is_editing (wbcg))
		return TRUE;

	/* Avoid recursion, and do not begin editing if a guru is up */
	if (wbcg->inside_editing || wbc_gtk_get_guru (wbcg) != NULL)
		return TRUE;
	wbcg->inside_editing = TRUE;

	wbv = wb_control_view (GNM_WBC (wbcg));
	sv = wb_control_cur_sheet_view (GNM_WBC (wbcg));
	scg = wbcg_cur_scg (wbcg);

	col = sv->edit_pos.col;
	row = sv->edit_pos.row;

	/* don't edit a locked cell */
	/* TODO : extend this to disable edits that cannot succeed
	 * like editing a single cell of an array.  I think we have enough
	 * information if we look at the selection.
	 */
	if (wb_view_is_protected (wbv, TRUE) &&
	    gnm_style_get_contents_locked (sheet_style_get (sv->sheet, col, row))) {
		char *pos =  g_strdup_printf ( _("%s!%s is locked"),
			sv->sheet->name_quoted, cell_coord_name (col, row));
		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg), pos,
			wb_view_is_protected (wbv, FALSE)
			 ? _("Unprotect the workbook to enable editing.")
			 : _("Unprotect the sheet to enable editing."));
		wbcg->inside_editing = FALSE;
		g_free (pos);
		return FALSE;
	}

	cell = sheet_cell_get (sv->sheet, col, row);
	if (cell &&
	    warn_on_text_format &&
	    go_format_is_text (gnm_cell_get_format (cell)) &&
	    (gnm_cell_has_expr (cell) || !VALUE_IS_STRING (cell->value))) {
		gint res; /* Using GtkResponseType would yield a warning on the switch */
		GtkWidget *check;
		GtkWidget *align;

		GtkWidget *d = gnm_message_dialog_create
			(wbcg_toplevel (wbcg),
			 GTK_DIALOG_DESTROY_WITH_PARENT,
			 GTK_MESSAGE_WARNING,
			 _("You are about to edit a cell with \"text\" format."),
			 _("The cell does not currently contain text, though, so if "
			   "you go on editing then the contents will be turned into "
			   "text."));
		gtk_dialog_add_button (GTK_DIALOG (d), GTK_STOCK_EDIT, GTK_RESPONSE_OK);
		go_gtk_dialog_add_button
			(GTK_DIALOG (d), _("Remove format"), GTK_STOCK_REMOVE,
			 GNM_RESPONSE_REMOVE);
		gtk_dialog_add_button (GTK_DIALOG (d), GNM_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
		gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_CANCEL);

		check = gtk_check_button_new_with_label (_("Show this dialog next time."));
		g_signal_connect (check, "toggled", G_CALLBACK (cb_warn_toggled), &warn_on_text_format);
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
		align = gtk_alignment_new (0.5, 0.5, 0, 0);
		gtk_container_add (GTK_CONTAINER (align), check);
		gtk_widget_show_all (align);
		gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), align, TRUE, TRUE, 0);
		res = go_gtk_dialog_run (GTK_DIALOG (d), wbcg_toplevel (wbcg));

		switch (res) {
		case GNM_RESPONSE_REMOVE: {
			GnmStyle *style = gnm_style_new ();
			gnm_style_set_format (style, go_format_general ());
			if (!cmd_selection_format (GNM_WBC (wbcg),
						   style, NULL, NULL))
				break;
			/* Fall through.  */
		}
		default:
		case GTK_RESPONSE_CANCEL:
			wbcg->inside_editing = FALSE;
			return FALSE;
		case GTK_RESPONSE_OK:
			break;
		}
	}

	gnm_app_clipboard_unant ();

	if (blankp)
		gtk_entry_set_text (wbcg_get_entry (wbcg), "");
	else if (cell != NULL) {
		gboolean quoted = FALSE;

		text = gnm_cell_get_text_for_editing (cell, sv->sheet, &quoted, &cursor_pos);

		if (text)
			gtk_entry_set_text (wbcg_get_entry (wbcg), text);

		if (cell->value != NULL) {
			GOFormat const *fmt = VALUE_FMT (cell->value);
			if (fmt != NULL && go_format_is_markup (fmt)) {
				PangoAttrList *markup =
					pango_attr_list_copy ((PangoAttrList *)go_format_get_markup (fmt));
				if (quoted)
					go_pango_attr_list_open_hole (markup, 0, 1);
				wbcg_edit_init_markup (wbcg, markup);
			}
		}
	}

	gnm_expr_entry_set_scg (wbcg->edit_line.entry, scg);
	gnm_expr_entry_set_flags (wbcg->edit_line.entry,
		GNM_EE_SHEET_OPTIONAL | GNM_EE_FORMULA_ONLY,
		GNM_EE_SINGLE_RANGE | GNM_EE_SHEET_OPTIONAL | GNM_EE_FORMULA_ONLY | GNM_EE_FORCE_REL_REF | GNM_EE_FORCE_ABS_REF);
	scg_edit_start (scg);

	/* Redraw the cell contents in case there was a span */
	sheet_redraw_region (sv->sheet, col, row, col, row);

	if (cursorp && /* autocompletion code will not work in the edit line */
	    wbv->do_auto_completion &&
	    (text == NULL || g_unichar_isalpha (g_utf8_get_char (text)))) {
		wbcg->auto_complete = gnm_complete_sheet_new (
			sv->sheet, col, row,
			workbook_edit_complete_notify, wbcg);
		wbcg->auto_completing = TRUE;
		wbcg->auto_max_size = 0;
	} else
		wbcg->auto_complete = NULL;

	/* Give the focus to the edit line */
	if (!cursorp)
		gtk_window_set_focus (wbcg_toplevel (wbcg),
			(GtkWidget *) wbcg_get_entry (wbcg));

	wbcg->editing = TRUE;
	wbcg->editing_sheet = sv->sheet;
	wbcg->editing_cell = cell;

	/* If this assert fails, it means editing was not shut down
	 * properly before
	 */
	g_return_val_if_fail (wbcg->edit_line.signal_changed == 0, TRUE);
	wbcg->edit_line.signal_changed = g_signal_connect (
		G_OBJECT (wbcg_get_entry (wbcg)),
		"changed",
		G_CALLBACK (cb_entry_changed), wbcg);
	wbcg->edit_line.signal_insert = g_signal_connect (
		G_OBJECT (wbcg_get_entry (wbcg)),
		"insert-text",
		G_CALLBACK (cb_entry_insert_text), wbcg);
	wbcg->edit_line.signal_delete = g_signal_connect (
		G_OBJECT (wbcg_get_entry (wbcg)),
		"delete-text",
		G_CALLBACK (cb_entry_delete_text), wbcg);
	wbcg->edit_line.signal_cursor_pos = g_signal_connect_swapped (
		G_OBJECT (wbcg_get_entry (wbcg)),
		"notify::cursor-position",
		G_CALLBACK (cb_entry_cursor_pos), wbcg);
	wbcg->edit_line.signal_selection_bound = g_signal_connect_swapped (
		G_OBJECT (wbcg_get_entry (wbcg)),
		"notify::selection-bound",
		G_CALLBACK (cb_entry_cursor_pos), wbcg);

	g_free (text);
	wb_control_update_action_sensitivity (GNM_WBC (wbcg));

	wbcg->inside_editing = FALSE;

	gtk_editable_set_position (GTK_EDITABLE (wbcg_get_entry (wbcg)), cursor_pos);

	return TRUE;
}