1 /*
2  * workbook-view.c: View functions for the workbook
3  *
4  * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
5  * Copyright (C) 2012 Morten Welinder (terra@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <workbook-view.h>
25 
26 #include <workbook-control-priv.h>
27 #include <workbook-priv.h>
28 #include <application.h>
29 #include <sheet.h>
30 #include <sheet-view.h>
31 #include <sheet-merge.h>
32 #include <sheet-style.h>
33 #include <func.h>
34 #include <expr.h>
35 #include <expr-name.h>
36 #include <value.h>
37 #include <ranges.h>
38 #include <selection.h>
39 #include <mstyle.h>
40 #include <validation.h>
41 #include <validation-combo.h>
42 #include <gnm-sheet-slicer.h>
43 #include <gnm-sheet-slicer-combo.h>
44 #include <position.h>
45 #include <cell.h>
46 #include <gutils.h>
47 #include <command-context.h>
48 #include <auto-format.h>
49 #include <sheet-object.h>
50 #include <gnumeric-conf.h>
51 
52 #include <goffice/goffice.h>
53 #include <gsf/gsf-meta-names.h>
54 #include <gsf/gsf-impl-utils.h>
55 #include <gsf/gsf-output-stdio.h>
56 #include <gsf/gsf-input.h>
57 #include <gnm-i18n.h>
58 #include <glib/gstdio.h>
59 #include <string.h>
60 #include <unistd.h>
61 #include <stdlib.h>
62 #include <mathfunc.h>
63 
64 #ifdef G_OS_WIN32
65 #include <windows.h>
66 #endif
67 
68 /**
69  * WorkbookView:
70  * @wb_controls: (element-type WorkbookControl):
71  **/
72 enum {
73 	PROP_0,
74 	PROP_AUTO_EXPR_FUNC,
75 	PROP_AUTO_EXPR_DESCR,
76 	PROP_AUTO_EXPR_MAX_PRECISION,
77 	PROP_AUTO_EXPR_VALUE,
78 	PROP_AUTO_EXPR_EVAL_POS,
79 	PROP_SHOW_HORIZONTAL_SCROLLBAR,
80 	PROP_SHOW_VERTICAL_SCROLLBAR,
81 	PROP_SHOW_NOTEBOOK_TABS,
82 	PROP_SHOW_FUNCTION_CELL_MARKERS,
83 	PROP_SHOW_EXTENSION_MARKERS,
84 	PROP_DO_AUTO_COMPLETION,
85 	PROP_PROTECTED,
86 	PROP_PREFERRED_WIDTH,
87 	PROP_PREFERRED_HEIGHT,
88 	PROP_WORKBOOK
89 };
90 
91 /* WorkbookView signals */
92 enum {
93 	LAST_SIGNAL
94 };
95 
96 /**
97  * wb_view_get_workbook:
98  * @wbv: #WorkbookView
99  *
100  * Returns: (transfer none): the #Workbook associated with @wbv
101  **/
102 Workbook *
wb_view_get_workbook(WorkbookView const * wbv)103 wb_view_get_workbook (WorkbookView const *wbv)
104 {
105 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
106 	return wbv->wb;
107 }
108 
109 /**
110  * wb_view_get_doc:
111  * @wbv: #WorkbookView
112  *
113  * Returns: (transfer none): the #Workbook associated with @wbv cast to a #GODoc
114  **/
115 GODoc *
wb_view_get_doc(WorkbookView const * wbv)116 wb_view_get_doc (WorkbookView const *wbv)
117 {
118 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
119 	return GO_DOC (wbv->wb);
120 }
121 
122 /**
123  * wb_view_get_index_in_wb:
124  * @wbv: #WorkbookView
125  *
126  * Returns 0 based index of wbv within workbook, or -1 if there is no workbook.
127  **/
128 int
wb_view_get_index_in_wb(WorkbookView const * wbv)129 wb_view_get_index_in_wb (WorkbookView const *wbv)
130 {
131 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), -1);
132 	if (NULL != wbv->wb) {
133 		unsigned i = wbv->wb->wb_views->len;
134 		while (i-- > 0)
135 			if (g_ptr_array_index (wbv->wb->wb_views, i) == wbv)
136 				return i;
137 	}
138 	return -1;
139 }
140 
141 /**
142  * wb_view_cur_sheet:
143  * @wbv: #WorkbookView
144  *
145  * Returns: (transfer none): the current sheet.
146  **/
147 Sheet *
wb_view_cur_sheet(WorkbookView const * wbv)148 wb_view_cur_sheet (WorkbookView const *wbv)
149 {
150 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
151 	return wbv->current_sheet;
152 }
153 
154 /**
155  * wb_view_cur_sheet_view:
156  * @wbv: #WorkbookView
157  *
158  * Returns: (transfer none): the current sheet view.
159  **/
160 SheetView *
wb_view_cur_sheet_view(WorkbookView const * wbv)161 wb_view_cur_sheet_view (WorkbookView const *wbv)
162 {
163 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
164 	return wbv->current_sheet_view;
165 }
166 
167 void
wb_view_sheet_focus(WorkbookView * wbv,Sheet * sheet)168 wb_view_sheet_focus (WorkbookView *wbv, Sheet *sheet)
169 {
170 	if (wbv->current_sheet != sheet) {
171 		/* Make sure the sheet has been attached */
172 		g_return_if_fail (sheet == NULL || sheet->index_in_wb >= 0);
173 
174 #if 0
175 		g_print ("Focus %s\n", sheet ? sheet->name_quoted : "-");
176 #endif
177 
178 		wbv->current_sheet = sheet;
179 		wbv->current_sheet_view = sheet_get_view (sheet, wbv);
180 
181 		WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control,
182 			wb_control_sheet_focus (control, sheet););
183 
184 		wb_view_selection_desc (wbv, TRUE, NULL);
185 		wb_view_edit_line_set (wbv, NULL);
186 		wb_view_style_feedback (wbv);
187 		wb_view_menus_update (wbv);
188 		wb_view_auto_expr_recalc (wbv);
189 	}
190 }
191 
192 void
wb_view_sheet_add(WorkbookView * wbv,Sheet * new_sheet)193 wb_view_sheet_add (WorkbookView *wbv, Sheet *new_sheet)
194 {
195 	SheetView *new_view;
196 
197 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
198 
199 	new_view = gnm_sheet_view_new (new_sheet, wbv);
200 
201 	WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control,
202 		wb_control_sheet_add (control, new_view););
203 
204 	g_object_unref (new_view);
205 
206 	if (wbv->current_sheet == NULL)
207 		wb_view_sheet_focus (wbv, new_sheet);
208 }
209 
210 gboolean
wb_view_is_protected(WorkbookView * wbv,gboolean check_sheet)211 wb_view_is_protected (WorkbookView *wbv, gboolean check_sheet)
212 {
213 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), FALSE);
214 
215 	return wbv->is_protected || (check_sheet &&
216 		wbv->current_sheet != NULL && wbv->current_sheet->is_protected);
217 }
218 
219 void
wb_view_set_attribute(WorkbookView * wbv,char const * name,char const * value)220 wb_view_set_attribute (WorkbookView *wbv, char const *name, char const *value)
221 {
222 	gboolean res;
223 	GObject *obj;
224 	const char *tname;
225 
226 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
227 	g_return_if_fail (name != NULL);
228 	g_return_if_fail (value != NULL);
229 
230 	obj = G_OBJECT (wbv);
231 	res = !g_ascii_strcasecmp (value, "TRUE");
232 
233 	if (strncmp (name, "WorkbookView::", 14) == 0)
234 		tname = name + 14;
235 	else if (strncmp (name, "Workbook::", 10) == 0)
236 		/* Some old files have this.  */
237 		tname = name + 10;
238 	else
239 		tname = "nope";
240 
241 	if (!strcmp (tname , "show_horizontal_scrollbar"))
242 		g_object_set (obj, "show_horizontal_scrollbar", res, NULL);
243 	else if (!strcmp (tname , "show_vertical_scrollbar"))
244 		g_object_set (obj, "show_vertical_scrollbar", res, NULL);
245 	else if (!strcmp (tname , "show_notebook_tabs"))
246 		g_object_set (obj, "show_notebook_tabs", res, NULL);
247 	else if (!strcmp (tname , "show_function_cell_markers"))
248 		g_object_set (obj, "show_function_cell_markers", res, NULL);
249 	else if (!strcmp (tname , "show_extension_markers"))
250 		g_object_set (obj, "show_extension_markers", res, NULL);
251 	else if (!strcmp (tname , "do_auto_completion"))
252 		g_object_set (obj, "do_auto_completion", res, NULL);
253 	else if (!strcmp (tname , "is_protected"))
254 		g_object_set (obj, "protected", res, NULL);
255 	else
256 		g_warning ("WorkbookView unknown arg '%s'", name);
257 }
258 
259 void
wb_view_preferred_size(WorkbookView * wbv,int w,int h)260 wb_view_preferred_size (WorkbookView *wbv, int w, int h)
261 {
262 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
263 
264 	if (w <= 0)
265 		w = 768; /* use default */
266 	if (h <= 0)
267 		h = 768; /* use default */
268 
269 	g_object_set (G_OBJECT (wbv),
270 		      "preferred-width", w,
271 		      "preferred-height", h,
272 		      NULL);
273 }
274 
275 void
wb_view_style_feedback(WorkbookView * wbv)276 wb_view_style_feedback (WorkbookView *wbv)
277 {
278 	SheetView *sv;
279 	GnmStyle const *style;
280 	GnmSheetSlicer const *dslicer;
281 	GODataSlicerField *dsfield;
282 	GnmValidation const *val;
283 	GOFormat const *fmt_style, *fmt_cell;
284 	GnmCell *cell;
285 	gboolean update_controls = TRUE;
286 
287 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
288 
289 	sv = wbv->current_sheet_view;
290 	if (sv == NULL)
291 		return;
292 
293 	style = sheet_style_get (sv->sheet,
294 		sv->edit_pos.col, sv->edit_pos.row);
295 	fmt_style = gnm_style_get_format (style);
296 	if (go_format_is_general (fmt_style) &&
297 	    (cell = sheet_cell_get (sv->sheet, sv->edit_pos.col, sv->edit_pos.row)) &&
298 	    cell->value && VALUE_FMT (cell->value))
299 		fmt_cell = VALUE_FMT (cell->value);
300 	else
301 		fmt_cell = fmt_style;
302 
303 	if (go_format_eq (fmt_cell, fmt_style)) {
304 		if (style == wbv->current_style)
305 			update_controls = FALSE;
306 		gnm_style_ref (style);
307 	} else {
308 		GnmStyle *tmp = gnm_style_dup (style);
309 		gnm_style_set_format (tmp, fmt_cell);
310 		style = tmp;
311 	}
312 
313 	if (wbv->current_style != NULL)
314 		gnm_style_unref (wbv->current_style);
315 	wbv->current_style = style;
316 
317 	if (wbv->in_cell_combo != NULL) {
318 		sheet_object_clear_sheet (wbv->in_cell_combo);
319 		g_object_unref (wbv->in_cell_combo);
320 		wbv->in_cell_combo = NULL;
321 	}
322 
323 	if (gnm_style_is_element_set (style, MSTYLE_VALIDATION) &&
324 	    NULL != (val = gnm_style_get_validation (style)) &&
325 	    val->type == GNM_VALIDATION_TYPE_IN_LIST &&
326 	    val->use_dropdown)
327 		wbv->in_cell_combo = gnm_validation_combo_new (val, sv);
328 	else if (NULL != (dslicer = gnm_sheet_slicers_at_pos (sv->sheet, &sv->edit_pos)) &&
329 		   NULL != (dsfield = gnm_sheet_slicer_field_header_at_pos (dslicer, &sv->edit_pos)))
330 		wbv->in_cell_combo = g_object_new (gnm_sheet_slicer_combo_get_type (),
331 						   "sheet-view", sv,
332 						   "field",	 dsfield,
333 						   NULL);
334 
335 	if (NULL != wbv->in_cell_combo)
336 	{
337 		const double a_offsets [4] = { 0., 0., 1., 1. };
338 		SheetObjectAnchor  anchor;
339 		GnmRange corner;
340 		GnmRange const *r;
341 		if (NULL == (r = gnm_sheet_merge_contains_pos (sv->sheet, &sv->edit_pos)))
342 			r = range_init_cellpos (&corner, &sv->edit_pos);
343 		sheet_object_anchor_init (&anchor, r, a_offsets, GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
344 		sheet_object_set_anchor (wbv->in_cell_combo, &anchor);
345 		sheet_object_set_sheet (wbv->in_cell_combo, sv->sheet);
346 	}
347 
348 	if (update_controls) {
349 		WORKBOOK_VIEW_FOREACH_CONTROL(wbv, control,
350 			wb_control_style_feedback (control, NULL););
351 	}
352 }
353 
354 void
wb_view_menus_update(WorkbookView * wbv)355 wb_view_menus_update (WorkbookView *wbv)
356 {
357 	Sheet *sheet;
358 
359 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
360 
361 	sheet = wbv->current_sheet;
362 	if (sheet != NULL) {
363 		WORKBOOK_VIEW_FOREACH_CONTROL (wbv, wbc, {
364 			wb_control_menu_state_update (wbc, MS_ALL);
365 			wb_control_update_action_sensitivity (wbc);
366 		});
367 	}
368 }
369 
370 /**
371  * wb_view_selection_desc:
372  * @wbv: The view
373  * @use_pos:
374  * @wbc: (allow-none): A #WorkbookControl
375  *
376  * Load the edit line with the value of the cell in sheet's edit_pos.
377  *
378  * Calculate what to display on the edit line then display it either in the
379  * control @wbc, or if that is %NULL, in all controls.
380  */
381 void
wb_view_selection_desc(WorkbookView * wbv,gboolean use_pos,WorkbookControl * wbc)382 wb_view_selection_desc (WorkbookView *wbv, gboolean use_pos,
383 			WorkbookControl *wbc)
384 {
385 	SheetView *sv;
386 
387 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
388 
389 	sv = wbv->current_sheet_view;
390 	if (sv != NULL) {
391 		char buffer [10 + 2 * 4 * sizeof (int)];
392 		char const *sel_descr = buffer;
393 		GnmRange const *r, *m;
394 
395 		g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
396 		g_return_if_fail (sv->selections);
397 
398 		r = selection_first_range (sv, NULL, NULL);
399 
400 		if (use_pos || range_is_singleton (r) ||
401 		    (NULL != (m = gnm_sheet_merge_is_corner (sv->sheet, &r->start)) &&
402 		     range_equal (r, m))) {
403 			sel_descr = sheet_names_check (sv->sheet, r);
404 			if (sel_descr == NULL) {
405 				GnmParsePos pp;
406 				parse_pos_init_editpos (&pp, sv);
407 				sel_descr = parsepos_as_string (&pp);
408 			}
409 		} else {
410 			int rows = r->end.row - r->start.row + 1;
411 			int cols = r->end.col - r->start.col + 1;
412 
413 			if (rows == gnm_sheet_get_max_rows (sv->sheet))
414         /* Translators: "%dC" is a very short format to indicate the number of full columns */
415 				snprintf (buffer, sizeof (buffer), _("%dC"), cols);
416 			else if (cols == gnm_sheet_get_max_cols (sv->sheet))
417         /* Translators: "%dR" is a very short format to indicate the number of full rows */
418 				snprintf (buffer, sizeof (buffer), _("%dR"), rows);
419 			else
420         /* Translators: "%dR x %dC" is a very short format to indicate the number of rows and columns */
421 				snprintf (buffer, sizeof (buffer), _("%dR x %dC"),
422 					  rows, cols);
423 		}
424 
425 		if (wbc == NULL) {
426 			WORKBOOK_VIEW_FOREACH_CONTROL (wbv, wbc,
427 				wb_control_selection_descr_set (wbc, sel_descr););
428 		} else
429 			wb_control_selection_descr_set (wbc, sel_descr);
430 	}
431 }
432 
433 /**
434  * wb_view_edit_line_set:
435  * @wbv: The view
436  * @wbc: (allow-none): A #WorkbookControl
437  *
438  * Load the edit line with the value of the cell in @sheet's edit_pos.
439  *
440  * Calculate what to display on the edit line then display it either in the
441  * control @wbc, or if that is %NULL, in all controls.
442  */
443 void
wb_view_edit_line_set(WorkbookView * wbv,WorkbookControl * wbc)444 wb_view_edit_line_set (WorkbookView *wbv, WorkbookControl *wbc)
445 {
446 	SheetView *sv;
447 
448 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
449 
450 	sv = wbv->current_sheet_view;
451 	if (sv != NULL) {
452 		char *text;
453 		Sheet *sheet = sv->sheet;
454 		GnmCell const *cell = sheet_cell_get (sheet,
455 			sv->edit_pos.col, sv->edit_pos.row);
456 
457 		if (NULL != cell) {
458 			text = gnm_cell_get_text_for_editing (cell, NULL, NULL);
459 
460 			if (gnm_cell_has_expr (cell)) {
461 				GnmExprTop const *texpr = cell->base.texpr;
462 				GnmCell const *corner = NULL;
463 				int x = 0, y = 0;
464 
465 				/*
466 				 * If this is part of an array we add '{' '}'
467 				 * and size information to the display.  That
468 				 * is not actually part of the parsable
469 				 * expression, but it is a useful extension to
470 				 * the simple '{' '}' that MS excel(tm) uses.
471 				 */
472 				if (gnm_expr_top_is_array_corner (texpr))
473 					corner = cell;
474 				else if (gnm_expr_top_is_array_elem (texpr, &x, &y)) {
475 					corner = sheet_cell_get
476 						(sheet,
477 						 cell->pos.col - x,
478 						 cell->pos.row - y);
479 				}
480 
481 				if (corner) {
482 					int cols, rows;
483 					char *tmp;
484 
485 					gnm_expr_top_get_array_size (corner->base.texpr, &cols, &rows);
486 
487 					tmp = g_strdup_printf
488 						("{%s}(%d%c%d)[%d][%d]",
489 						 text,
490 						 cols, go_locale_get_arg_sep (), rows,
491 						 x, y);
492 					g_free (text);
493 					text = tmp;
494 				}
495 			}
496 		} else
497 			text = g_strdup ("");
498 
499 		if (wbc == NULL) {
500 			WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control,
501 				wb_control_edit_line_set (control, text););
502 		} else
503 			wb_control_edit_line_set (wbc, text);
504 
505 		g_free (text);
506 	}
507 }
508 
509 static void
accumulate_regions(SheetView * sv,GnmRange const * r,gpointer closure)510 accumulate_regions (SheetView *sv,  GnmRange const *r, gpointer closure)
511 {
512 	GnmExprList	**selection = closure;
513 	GnmCellRef a, b;
514 
515 	a.sheet = b.sheet = sv_sheet (sv);
516 	a.col_relative = a.row_relative = b.col_relative = b.row_relative = FALSE;
517 	a.col = r->start.col;
518 	a.row = r->start.row;
519 	b.col = r->end.col;
520 	b.row = r->end.row;
521 
522 	*selection = gnm_expr_list_prepend (*selection,
523 		gnm_expr_new_constant (value_new_cellrange_unsafe (&a, &b)));
524 }
525 
526 void
wb_view_auto_expr_recalc(WorkbookView * wbv)527 wb_view_auto_expr_recalc (WorkbookView *wbv)
528 {
529 	GnmExprList	*selection = NULL;
530 	GnmValue	*v;
531 	SheetView	*sv;
532 	GnmExprTop const *texpr;
533 	GnmEvalPos      ep;
534 
535 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
536 
537 	sv = wb_view_cur_sheet_view (wbv);
538 	if (wbv->current_sheet == NULL ||
539 	    sv == NULL)
540 		return;
541 
542 	if (wbv->auto_expr.dep.base.sheet != NULL &&
543 	    wbv->auto_expr.dep.base.texpr != NULL) {
544 		texpr = wbv->auto_expr.dep.base.texpr;
545 		gnm_expr_top_ref (texpr);
546 	} else if (wbv->auto_expr.func != NULL) {
547 		sv_selection_apply (sv, &accumulate_regions, FALSE, &selection);
548 		texpr = gnm_expr_top_new
549 			(gnm_expr_new_funcall (wbv->auto_expr.func, selection));
550 	} else {
551 		texpr = gnm_expr_top_new_constant (value_new_string (""));
552 	}
553 
554 	eval_pos_init_sheet (&ep, wbv->current_sheet);
555 
556 	v = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
557 	if (v) {
558 		if (wbv->auto_expr.use_max_precision)
559 			value_set_fmt (v, NULL);
560 		else if (!VALUE_FMT (v)) {
561 			GOFormat const *fmt = gnm_auto_style_format_suggest (texpr, &ep);
562 			value_set_fmt (v, fmt);
563 			go_format_unref (fmt);
564 		}
565 	}
566 	g_object_set (wbv, "auto-expr-value", v, NULL);
567 	value_release (v);
568 	gnm_expr_top_unref (texpr);
569 }
570 
571 /* perform whatever initialization of a control that is necessary when it
572  * finally gets assigned to a view with a workbook */
573 static void
wb_view_init_control(G_GNUC_UNUSED WorkbookControl * wbc)574 wb_view_init_control (G_GNUC_UNUSED WorkbookControl *wbc)
575 {
576 }
577 
578 void
wb_view_attach_control(WorkbookView * wbv,WorkbookControl * wbc)579 wb_view_attach_control (WorkbookView *wbv, WorkbookControl *wbc)
580 {
581 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
582 	g_return_if_fail (GNM_IS_WBC (wbc));
583 	g_return_if_fail (wb_control_view (wbc) == NULL);
584 
585 	if (wbv->wb_controls == NULL)
586 		wbv->wb_controls = g_ptr_array_new ();
587 	g_ptr_array_add (wbv->wb_controls, wbc);
588 	g_object_set (G_OBJECT (wbc), "view", wbv, NULL);
589 
590 	if (wbv->wb != NULL)
591 		wb_view_init_control (wbc);
592 }
593 
594 void
wb_view_detach_control(WorkbookControl * wbc)595 wb_view_detach_control (WorkbookControl *wbc)
596 {
597 	g_return_if_fail (GNM_IS_WBC (wbc));
598 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wb_control_view (wbc)));
599 
600 	g_ptr_array_remove (wbc->wb_view->wb_controls, wbc);
601 	if (wbc->wb_view->wb_controls->len == 0) {
602 		g_ptr_array_free (wbc->wb_view->wb_controls, TRUE);
603 		wbc->wb_view->wb_controls = NULL;
604 	}
605 	g_object_set (G_OBJECT (wbc), "view", NULL, NULL);
606 }
607 
608 static GObjectClass *parent_class;
609 
610 static void
wb_view_auto_expr_func(WorkbookView * wbv,GnmFunc * func)611 wb_view_auto_expr_func (WorkbookView *wbv, GnmFunc *func)
612 {
613 	if (wbv->auto_expr.func == func)
614 		return;
615 
616 	if (wbv->auto_expr.func)
617 		gnm_func_dec_usage (wbv->auto_expr.func);
618 
619 	if (func)
620 		gnm_func_inc_usage (func);
621 	wbv->auto_expr.func = func;
622 
623 	wb_view_auto_expr_recalc (wbv);
624 }
625 
626 static void
wb_view_auto_expr_descr(WorkbookView * wbv,const char * descr)627 wb_view_auto_expr_descr (WorkbookView *wbv, const char *descr)
628 {
629 	char *s;
630 
631 	if (go_str_compare (descr, wbv->auto_expr.descr) == 0)
632 		return;
633 
634 	s = g_strdup (descr);
635 	g_free (wbv->auto_expr.descr);
636 	wbv->auto_expr.descr = s;
637 
638 	wb_view_auto_expr_recalc (wbv);
639 }
640 
641 static void
wb_view_auto_expr_precision(WorkbookView * wbv,gboolean use_max_precision)642 wb_view_auto_expr_precision (WorkbookView *wbv, gboolean use_max_precision)
643 {
644 	use_max_precision = !!use_max_precision;
645 
646 	if (wbv->auto_expr.use_max_precision == use_max_precision)
647 		return;
648 
649 	wbv->auto_expr.use_max_precision = use_max_precision;
650 
651 	wb_view_auto_expr_recalc (wbv);
652 }
653 
654 static void
wb_view_auto_expr_value(WorkbookView * wbv,const GnmValue * value)655 wb_view_auto_expr_value (WorkbookView *wbv, const GnmValue *value)
656 {
657 	value_release (wbv->auto_expr.value);
658 	wbv->auto_expr.value = value_dup (value);
659 }
660 
661 static void
cb_clear_auto_expr_sheet(WorkbookView * wbv)662 cb_clear_auto_expr_sheet (WorkbookView *wbv)
663 {
664 	g_object_set (G_OBJECT (wbv),
665 		      "auto-expr-eval-pos", NULL,
666 		      NULL);
667 }
668 
669 static void
wb_view_auto_expr_eval_pos(WorkbookView * wbv,GnmEvalPos const * ep)670 wb_view_auto_expr_eval_pos (WorkbookView *wbv, GnmEvalPos const *ep)
671 {
672 	Sheet *sheet = ep ? ep->sheet : NULL;
673 
674 	if (wbv->auto_expr.sheet_detached_sig) {
675 		g_signal_handler_disconnect (wbv->auto_expr.dep.base.sheet,
676 					     wbv->auto_expr.sheet_detached_sig);
677 		wbv->auto_expr.sheet_detached_sig = 0;
678 	}
679 
680 	dependent_managed_set_expr (&wbv->auto_expr.dep, NULL);
681 	dependent_managed_set_sheet (&wbv->auto_expr.dep, sheet);
682 
683 	if (sheet) {
684 		GnmRange r;
685 		GnmValue *v;
686 		GnmExprTop const *texpr;
687 
688 		wbv->auto_expr.sheet_detached_sig = g_signal_connect_swapped (
689 			G_OBJECT (sheet),
690 			"detached-from-workbook",
691 			G_CALLBACK (cb_clear_auto_expr_sheet), wbv);
692 
693 		range_init_cellpos (&r, &ep->eval);
694 		v = value_new_cellrange_r (sheet, &r);
695 		texpr = gnm_expr_top_new_constant (v);
696 		dependent_managed_set_expr (&wbv->auto_expr.dep, texpr);
697 		gnm_expr_top_unref (texpr);
698 	}
699 
700 	wb_view_auto_expr_recalc (wbv);
701 }
702 
703 static void
wb_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)704 wb_view_set_property (GObject *object, guint property_id,
705 		      const GValue *value, GParamSpec *pspec)
706 {
707 	WorkbookView *wbv = (WorkbookView *)object;
708 
709 	switch (property_id) {
710 	case PROP_AUTO_EXPR_FUNC:
711 		wb_view_auto_expr_func (wbv, g_value_get_object (value));
712 		break;
713 	case PROP_AUTO_EXPR_DESCR:
714 		wb_view_auto_expr_descr (wbv, g_value_get_string (value));
715 		break;
716 	case PROP_AUTO_EXPR_MAX_PRECISION:
717 		wb_view_auto_expr_precision (wbv, g_value_get_boolean (value));
718 		break;
719 	case PROP_AUTO_EXPR_VALUE:
720 		wb_view_auto_expr_value (wbv, g_value_get_boxed (value));
721 		break;
722 	case PROP_AUTO_EXPR_EVAL_POS:
723 		wb_view_auto_expr_eval_pos (wbv, g_value_get_boxed (value));
724 		break;
725 	case PROP_SHOW_HORIZONTAL_SCROLLBAR:
726 		wbv->show_horizontal_scrollbar = !!g_value_get_boolean (value);
727 		break;
728 	case PROP_SHOW_VERTICAL_SCROLLBAR:
729 		wbv->show_vertical_scrollbar = !!g_value_get_boolean (value);
730 		break;
731 	case PROP_SHOW_NOTEBOOK_TABS:
732 		wbv->show_notebook_tabs = !!g_value_get_boolean (value);
733 		break;
734 	case PROP_SHOW_FUNCTION_CELL_MARKERS:
735 		wbv->show_function_cell_markers = !!g_value_get_boolean (value);
736 		if (wbv->current_sheet)
737 			sheet_redraw_all (wbv->current_sheet, FALSE);
738 		break;
739 	case PROP_SHOW_EXTENSION_MARKERS:
740 		wbv->show_extension_markers = !!g_value_get_boolean (value);
741 		if (wbv->current_sheet)
742 			sheet_redraw_all (wbv->current_sheet, FALSE);
743 		break;
744 	case PROP_DO_AUTO_COMPLETION:
745 		wbv->do_auto_completion = !!g_value_get_boolean (value);
746 		break;
747 	case PROP_PROTECTED:
748 		wbv->is_protected = !!g_value_get_boolean (value);
749 		break;
750 	case PROP_PREFERRED_WIDTH:
751 		wbv->preferred_width = g_value_get_int (value);
752 		break;
753 	case PROP_PREFERRED_HEIGHT:
754 		wbv->preferred_height = g_value_get_int (value);
755 		break;
756 	case PROP_WORKBOOK:
757 		wbv->wb = g_value_dup_object (value);
758 		break;
759 	default:
760 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
761 		break;
762 	}
763 }
764 
765 static void
wb_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)766 wb_view_get_property (GObject *object, guint property_id,
767 		      GValue *value, GParamSpec *pspec)
768 {
769 	WorkbookView *wbv = (WorkbookView *)object;
770 
771 	switch (property_id) {
772 	case PROP_AUTO_EXPR_FUNC:
773 		g_value_set_object (value, wbv->auto_expr.func);
774 		break;
775 	case PROP_AUTO_EXPR_DESCR:
776 		g_value_set_string (value, wbv->auto_expr.descr);
777 		break;
778 	case PROP_AUTO_EXPR_MAX_PRECISION:
779 		g_value_set_boolean (value, wbv->auto_expr.use_max_precision);
780 		break;
781 	case PROP_AUTO_EXPR_VALUE:
782 		g_value_set_boxed (value, wbv->auto_expr.value);
783 		break;
784 	case PROP_SHOW_HORIZONTAL_SCROLLBAR:
785 		g_value_set_boolean (value, wbv->show_horizontal_scrollbar);
786 		break;
787 	case PROP_SHOW_VERTICAL_SCROLLBAR:
788 		g_value_set_boolean (value, wbv->show_vertical_scrollbar);
789 		break;
790 	case PROP_SHOW_NOTEBOOK_TABS:
791 		g_value_set_boolean (value, wbv->show_notebook_tabs);
792 		break;
793 	case PROP_SHOW_FUNCTION_CELL_MARKERS:
794 		g_value_set_boolean (value, wbv->show_function_cell_markers);
795 		break;
796 	case PROP_SHOW_EXTENSION_MARKERS:
797 		g_value_set_boolean (value, wbv->show_extension_markers);
798 		break;
799 	case PROP_DO_AUTO_COMPLETION:
800 		g_value_set_boolean (value, wbv->do_auto_completion);
801 		break;
802 	case PROP_PROTECTED:
803 		g_value_set_boolean (value, wbv->is_protected);
804 		break;
805 	case PROP_PREFERRED_WIDTH:
806 		g_value_set_int (value, wbv->preferred_width);
807 		break;
808 	case PROP_PREFERRED_HEIGHT:
809 		g_value_set_int (value, wbv->preferred_height);
810 		break;
811 	case PROP_WORKBOOK:
812 		g_value_set_object (value, wbv->wb);
813 		break;
814 	default:
815 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
816 		break;
817 	}
818 }
819 
820 void
wb_view_detach_from_workbook(WorkbookView * wbv)821 wb_view_detach_from_workbook (WorkbookView *wbv)
822 {
823 	g_return_if_fail (GNM_IS_WORKBOOK_VIEW (wbv));
824 
825 	if (wbv->wb) {
826 		workbook_detach_view (wbv);
827 		wbv->wb = NULL;
828 		wbv->current_sheet = NULL;
829 	}
830 }
831 
832 static GObject *
wb_view_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)833 wb_view_constructor (GType type,
834 		     guint n_construct_properties,
835 		     GObjectConstructParam *construct_params)
836 {
837 	GObject *obj;
838 	WorkbookView *wbv;
839 	int i;
840 
841 	obj = parent_class->constructor
842 		(type, n_construct_properties, construct_params);
843 	wbv = GNM_WORKBOOK_VIEW (obj);
844 
845 	if (wbv->wb == NULL)
846 		wbv->wb = workbook_new ();
847 
848 	workbook_attach_view (wbv);
849 
850 	for (i = 0 ; i < workbook_sheet_count (wbv->wb); i++)
851 		wb_view_sheet_add (wbv, workbook_sheet_by_index (wbv->wb, i));
852 
853 	if (wbv->auto_expr.func == NULL) {
854 		wb_view_auto_expr_func (wbv, gnm_func_lookup ("sum", NULL));
855 		wb_view_auto_expr_descr (wbv, _("Sum"));
856 	}
857 
858 	return obj;
859 }
860 
861 static void
wb_view_dispose(GObject * object)862 wb_view_dispose (GObject *object)
863 {
864 	WorkbookView *wbv = GNM_WORKBOOK_VIEW (object);
865 
866 	if (wbv->wb_controls != NULL) {
867 		WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control, {
868 			wb_control_sheet_remove_all (control);
869 			wb_view_detach_control (control);
870 			g_object_unref (control);
871 		});
872 		if (wbv->wb_controls != NULL)
873 			g_warning ("Unexpected left-over controls");
874 	}
875 
876 	/* The order of these are important.  Make sure not to leak the value.  */
877 	wb_view_auto_expr_descr (wbv, NULL);
878 	wb_view_auto_expr_eval_pos (wbv, NULL);
879 	wb_view_auto_expr_func (wbv, NULL);
880 	wb_view_auto_expr_value (wbv, NULL);
881 
882 	wb_view_detach_from_workbook (wbv);
883 
884 	if (wbv->current_style != NULL) {
885 		gnm_style_unref (wbv->current_style);
886 		wbv->current_style = NULL;
887 	}
888 	if (wbv->in_cell_combo != NULL) {
889 		sheet_object_clear_sheet (wbv->in_cell_combo);
890 		g_object_unref (wbv->in_cell_combo);
891 		wbv->in_cell_combo = NULL;
892 	}
893 
894 	parent_class->dispose (object);
895 }
896 
897 
898 static void
workbook_view_class_init(GObjectClass * gobject_class)899 workbook_view_class_init (GObjectClass *gobject_class)
900 {
901 	parent_class = g_type_class_peek_parent (gobject_class);
902 
903 	gobject_class->constructor = wb_view_constructor;
904 	gobject_class->set_property = wb_view_set_property;
905 	gobject_class->get_property = wb_view_get_property;
906 	gobject_class->dispose = wb_view_dispose;
907 
908         g_object_class_install_property
909 		(gobject_class,
910 		 PROP_AUTO_EXPR_FUNC,
911 		 g_param_spec_object ("auto-expr-func",
912 				      P_("Auto-expression function"),
913 				      P_("The automatically computed sheet function."),
914 				      GNM_FUNC_TYPE,
915 				      GSF_PARAM_STATIC |
916 				      G_PARAM_READWRITE));
917         g_object_class_install_property
918 		(gobject_class,
919 		 PROP_AUTO_EXPR_DESCR,
920 		 g_param_spec_string ("auto-expr-descr",
921 				      P_("Auto-expression description"),
922 				      P_("Description of the automatically computed sheet function."),
923 				      NULL,
924 				      GSF_PARAM_STATIC |
925 				      G_PARAM_READWRITE));
926         g_object_class_install_property
927 		(gobject_class,
928 		 PROP_AUTO_EXPR_MAX_PRECISION,
929 		 g_param_spec_boolean ("auto-expr-max-precision",
930 				       P_("Auto-expression maximum precision"),
931 				       P_("Use maximum available precision for auto-expressions"),
932 				       FALSE,
933 				       GSF_PARAM_STATIC |
934 				       G_PARAM_READWRITE));
935         g_object_class_install_property
936 		(gobject_class,
937 		 PROP_AUTO_EXPR_VALUE,
938 		 g_param_spec_boxed ("auto-expr-value",
939 				     P_("Auto-expression value"),
940 				     P_("The current value of the auto-expression."),
941 				     gnm_value_get_type (),
942 				     GSF_PARAM_STATIC |
943 				     G_PARAM_READWRITE));
944         g_object_class_install_property
945 		(gobject_class,
946 		 PROP_AUTO_EXPR_EVAL_POS,
947 		 g_param_spec_boxed ("auto-expr-eval-pos",
948 				     P_("Auto-expression position"),
949 				     P_("The cell position to track."),
950 				     gnm_eval_pos_get_type (),
951 				     GSF_PARAM_STATIC |
952 				     G_PARAM_WRITABLE));
953         g_object_class_install_property
954 		(gobject_class,
955 		 PROP_SHOW_HORIZONTAL_SCROLLBAR,
956 		 g_param_spec_boolean ("show-horizontal-scrollbar",
957 				       P_("Show horizontal scrollbar"),
958 				       P_("Show the horizontal scrollbar"),
959 				       TRUE,
960 				       GSF_PARAM_STATIC |
961 				       G_PARAM_READWRITE));
962         g_object_class_install_property
963 		(gobject_class,
964 		 PROP_SHOW_VERTICAL_SCROLLBAR,
965 		 g_param_spec_boolean ("show-vertical-scrollbar",
966 				       P_("Show vertical scrollbar"),
967 				       P_("Show the vertical scrollbar"),
968 				       TRUE,
969 				       GSF_PARAM_STATIC |
970 				       G_PARAM_READWRITE));
971         g_object_class_install_property
972 		(gobject_class,
973 		 PROP_SHOW_NOTEBOOK_TABS,
974 		 g_param_spec_boolean ("show-notebook-tabs",
975 				       P_("Show notebook tabs"),
976 				       P_("Show the notebook tabs for sheets"),
977 				       TRUE,
978 				       GSF_PARAM_STATIC |
979 				       G_PARAM_READWRITE));
980         g_object_class_install_property
981 		(gobject_class,
982 		 PROP_SHOW_FUNCTION_CELL_MARKERS,
983 		 g_param_spec_boolean ("show-function-cell-markers",
984 				       P_("Show formula cell markers"),
985 				       P_("Mark each cell containing a formula"),
986 				       FALSE,
987 				       GSF_PARAM_STATIC |
988 				       G_PARAM_READWRITE));
989         g_object_class_install_property
990 		(gobject_class,
991 		 PROP_SHOW_EXTENSION_MARKERS,
992 		 g_param_spec_boolean ("show-extension-markers",
993 				       P_("Show extension markers"),
994 				       P_("Mark each cell that fails to show the complete content"),
995 				       FALSE,
996 				       GSF_PARAM_STATIC |
997 				       G_PARAM_READWRITE));
998 	g_object_class_install_property
999 		(gobject_class,
1000 		 PROP_DO_AUTO_COMPLETION,
1001 		 g_param_spec_boolean ("do-auto-completion",
1002 				       P_("Do auto completion"),
1003 				       P_("Auto-complete text"),
1004 				       FALSE,
1005 				       GSF_PARAM_STATIC |
1006 				       G_PARAM_READWRITE));
1007         g_object_class_install_property
1008 		(gobject_class,
1009 		 PROP_PROTECTED,
1010 		 g_param_spec_boolean ("protected",
1011 				       P_("Protected"),
1012 				       P_("Is view protected?"),
1013 				       FALSE,
1014 				       GSF_PARAM_STATIC |
1015 				       G_PARAM_READWRITE));
1016         g_object_class_install_property
1017 		(gobject_class,
1018 		 PROP_PREFERRED_WIDTH,
1019 		 g_param_spec_int ("preferred-width",
1020 				   P_("Preferred width"),
1021 				   P_("Preferred width"),
1022 				   1, G_MAXINT, 1024,
1023 				   GSF_PARAM_STATIC |
1024 				   G_PARAM_READWRITE));
1025         g_object_class_install_property
1026 		(gobject_class,
1027 		 PROP_PREFERRED_HEIGHT,
1028 		 g_param_spec_int ("preferred-height",
1029 				   P_("Preferred height"),
1030 				   P_("Preferred height"),
1031 				   1, G_MAXINT, 768,
1032 				   GSF_PARAM_STATIC |
1033 				   G_PARAM_READWRITE));
1034         g_object_class_install_property
1035 		(gobject_class,
1036 		 PROP_WORKBOOK,
1037 		 g_param_spec_object ("workbook",
1038 				      P_("Workbook"),
1039 				      P_("Workbook"),
1040 				      GNM_WORKBOOK_TYPE,
1041 				      GSF_PARAM_STATIC |
1042 				      G_PARAM_CONSTRUCT_ONLY |
1043 				      G_PARAM_READWRITE));
1044 
1045 	parent_class = g_type_class_peek_parent (gobject_class);
1046 }
1047 
1048 static void
workbook_view_init(WorkbookView * wbv)1049 workbook_view_init (WorkbookView *wbv)
1050 {
1051 	wbv->show_horizontal_scrollbar = TRUE;
1052 	wbv->show_vertical_scrollbar = TRUE;
1053 	wbv->show_notebook_tabs = TRUE;
1054 	wbv->show_function_cell_markers =
1055 		gnm_conf_get_core_gui_cells_function_markers ();
1056 	wbv->show_extension_markers =
1057 		gnm_conf_get_core_gui_cells_extension_markers ();
1058 	wbv->do_auto_completion =
1059 		gnm_conf_get_core_gui_editing_autocomplete ();
1060 	wbv->is_protected = FALSE;
1061 
1062 	dependent_managed_init (&wbv->auto_expr.dep, NULL);
1063 }
1064 
GSF_CLASS(WorkbookView,workbook_view,workbook_view_class_init,workbook_view_init,GO_TYPE_VIEW)1065 GSF_CLASS (WorkbookView, workbook_view,
1066 	   workbook_view_class_init, workbook_view_init, GO_TYPE_VIEW)
1067 
1068 /**
1069  * workbook_view_new:
1070  * @wb: (allow-none) (transfer full): #Workbook
1071  *
1072  * Returns: A new #WorkbookView for @wb (or a fresh one if that is %NULL).
1073  **/
1074 WorkbookView *
1075 workbook_view_new (Workbook *wb)
1076 {
1077 	WorkbookView *wbv =
1078 		g_object_new (GNM_WORKBOOK_VIEW_TYPE, "workbook", wb, NULL);
1079 	if (wb) g_object_unref (wb);
1080 
1081 	return wbv;
1082 }
1083 
1084 /**
1085  * workbook_view_save_to_output:
1086  * @wbv: #WorkbookView
1087  * @fs: #GOFileSaver
1088  * @output: #GsfOutput
1089  * @io_context: #GOIOContext
1090  *
1091  * NOTE : Temporary api until we get the new output framework.
1092  **/
1093 void
workbook_view_save_to_output(WorkbookView * wbv,GOFileSaver const * fs,GsfOutput * output,GOIOContext * io_context)1094 workbook_view_save_to_output (WorkbookView *wbv, GOFileSaver const *fs,
1095 			      GsfOutput *output, GOIOContext *io_context)
1096 {
1097 	GError const *err;
1098 	char const   *msg;
1099 	GODoc *godoc = wb_view_get_doc (wbv);
1100 
1101 	if (go_doc_is_dirty (godoc))
1102 	  /* FIXME: we should be using the true modification time */
1103 	  gnm_insert_meta_date (godoc, GSF_META_NAME_DATE_MODIFIED);
1104 	go_file_saver_save (fs, io_context, GO_VIEW (wbv), output);
1105 
1106 	/* The plugin convention is unclear */
1107 	if (!gsf_output_is_closed (output))
1108 		gsf_output_close (output);
1109 
1110 	if (NULL == (err = gsf_output_error (output)))
1111 		return;
1112 	if (NULL == (msg = err->message))
1113 		msg = _("An unexplained error happened while saving.");
1114 	g_printerr ("  ==> %s\n", msg);
1115 	if (!go_io_error_occurred (io_context))
1116 		go_cmd_context_error_export (GO_CMD_CONTEXT (io_context), msg);
1117 }
1118 
1119 /**
1120  * workbook_view_save_to_uri:
1121  * @wbv: #WorkbookView
1122  * @fs: #GOFileSaver
1123  * @uri: destination URI
1124  * @io_context: #GOIOContext
1125  *
1126  **/
1127 void
workbook_view_save_to_uri(WorkbookView * wbv,GOFileSaver const * fs,char const * uri,GOIOContext * io_context)1128 workbook_view_save_to_uri (WorkbookView *wbv, GOFileSaver const *fs,
1129 			   char const *uri, GOIOContext *io_context)
1130 {
1131 	char   *msg = NULL;
1132 	GError *err = NULL;
1133 	GsfOutput *output = go_file_create (uri, &err);
1134 
1135 	if (output == NULL) {
1136 		if (NULL != err) {
1137 			msg = g_strdup_printf (_("Can't open '%s' for writing: %s"),
1138 						     uri, err->message);
1139 			g_error_free (err);
1140 		} else
1141 			msg = g_strdup_printf (_("Can't open '%s' for writing"), uri);
1142 
1143 		go_cmd_context_error_export (GO_CMD_CONTEXT (io_context), msg);
1144 		g_free (msg);
1145 	} else {
1146 		workbook_view_save_to_output (wbv, fs, output, io_context);
1147 		g_object_unref (output);
1148 	}
1149 }
1150 
1151 static GDateTime *
get_uri_modtime(GsfInput * input,const char * uri)1152 get_uri_modtime (GsfInput *input, const char *uri)
1153 {
1154 	GDateTime *modtime = NULL;
1155 
1156 	if (input) {
1157 		modtime = gsf_input_get_modtime (input);
1158 		if (modtime)
1159 			g_date_time_ref (modtime);
1160 	}
1161 
1162 	if (!modtime && uri)
1163 		modtime = go_file_get_modtime (uri);
1164 
1165 	if (gnm_debug_flag ("modtime")) {
1166 		char *s = modtime
1167 			? g_date_time_format (modtime, "%F %T")
1168 			: g_strdup ("?");
1169 		g_printerr ("Modtime of %s is %s\n", uri, s);
1170 		g_free (s);
1171 	}
1172 
1173 	return modtime;
1174 }
1175 
1176 /**
1177  * workbook_view_save_as:
1178  * @wbv: Workbook View
1179  * @fs: GOFileSaver object
1180  * @uri: URI to save as.
1181  * @cc: The #GOCmdContext that invoked the operation
1182  *
1183  * Saves @wbv and workbook it's attached to into @uri file using
1184  * @fs file saver.  If the format sufficiently advanced make it the saver
1185  * and update the uri.
1186  *
1187  * Return value: %TRUE if file was successfully saved and %FALSE otherwise.
1188  */
1189 gboolean
workbook_view_save_as(WorkbookView * wbv,GOFileSaver * fs,char const * uri,GOCmdContext * cc)1190 workbook_view_save_as (WorkbookView *wbv, GOFileSaver *fs, char const *uri,
1191 		       GOCmdContext *cc)
1192 {
1193 	GOIOContext *io_context;
1194 	Workbook  *wb;
1195 	gboolean has_error, has_warning;
1196 
1197 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), FALSE);
1198 	g_return_val_if_fail (GO_IS_FILE_SAVER (fs), FALSE);
1199 	g_return_val_if_fail (uri != NULL, FALSE);
1200 	g_return_val_if_fail (GO_IS_CMD_CONTEXT (cc), FALSE);
1201 
1202 	wb = wb_view_get_workbook (wbv);
1203 	g_object_ref (wb);
1204 	io_context = go_io_context_new (cc);
1205 
1206 	go_cmd_context_set_sensitive (cc, FALSE);
1207 	workbook_view_save_to_uri (wbv, fs, uri, io_context);
1208 	go_cmd_context_set_sensitive (cc, TRUE);
1209 
1210 	has_error   = go_io_error_occurred (io_context);
1211 	has_warning = go_io_warning_occurred (io_context);
1212 	if (!has_error) {
1213 		GOFileFormatLevel fl = go_file_saver_get_format_level (fs);
1214 		if (workbook_set_saveinfo (wb, fl, fs)) {
1215 			if (go_doc_set_uri (GO_DOC (wb), uri)) {
1216 				GDateTime *modtime;
1217 
1218 				go_doc_set_saved_state (GO_DOC (wb),
1219 							go_doc_get_state (GO_DOC (wb)));
1220 				go_doc_set_dirty (GO_DOC (wb), FALSE);
1221 				/* See 634792.  */
1222 				go_doc_set_pristine (GO_DOC (wb), FALSE);
1223 
1224 				modtime = get_uri_modtime (NULL, uri);
1225 				if (modtime) {
1226 					go_doc_set_modtime (GO_DOC (wb), modtime);
1227 					if (gnm_debug_flag ("modtime"))
1228 						g_printerr ("Modtime set\n");
1229 					g_date_time_unref (modtime);
1230 				}
1231 			}
1232 		} else
1233 			workbook_set_last_export_uri (wb, uri);
1234 	}
1235 	if (has_error || has_warning)
1236 		go_io_error_display (io_context);
1237 	g_object_unref (io_context);
1238 	g_object_unref (wb);
1239 
1240 	return !has_error;
1241 }
1242 
1243 /**
1244  * workbook_view_save:
1245  * @wbv: The view to save.
1246  * @cc: The #GOCmdContext that invoked the operation
1247  *
1248  * Saves @wbv and workbook it's attached to into file assigned to the
1249  * workbook using workbook's file saver. If the workbook has no file
1250  * saver assigned to it, default file saver is used instead.
1251  *
1252  * Return value: %TRUE if file was successfully saved and %FALSE otherwise.
1253  */
1254 gboolean
workbook_view_save(WorkbookView * wbv,GOCmdContext * context)1255 workbook_view_save (WorkbookView *wbv, GOCmdContext *context)
1256 {
1257 	GOIOContext	*io_context;
1258 	Workbook	*wb;
1259 	GOFileSaver	*fs;
1260 	gboolean has_error, has_warning;
1261 	char const *uri;
1262 
1263 	g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), FALSE);
1264 	g_return_val_if_fail (GO_IS_CMD_CONTEXT (context), FALSE);
1265 
1266 	wb = wb_view_get_workbook (wbv);
1267 	g_object_ref (wb);
1268 	uri = go_doc_get_uri (GO_DOC (wb));
1269 
1270 	fs = workbook_get_file_saver (wb);
1271 	if (fs == NULL)
1272 		fs = go_file_saver_get_default ();
1273 
1274 	io_context = go_io_context_new (context);
1275 	if (fs == NULL)
1276 		go_cmd_context_error_export (GO_CMD_CONTEXT (io_context),
1277 			_("Default file saver is not available."));
1278 	else {
1279 		char const *uri = go_doc_get_uri (GO_DOC (wb));
1280 		workbook_view_save_to_uri (wbv, fs, uri, io_context);
1281 	}
1282 
1283 	has_error   = go_io_error_occurred (io_context);
1284 	has_warning = go_io_warning_occurred (io_context);
1285 	if (!has_error) {
1286 		GDateTime *modtime = get_uri_modtime (NULL, uri);
1287 		go_doc_set_modtime (GO_DOC (wb), modtime);
1288 		if (gnm_debug_flag ("modtime"))
1289 			g_printerr ("Modtime set\n");
1290 		g_date_time_unref (modtime);
1291 		go_doc_set_saved_state (GO_DOC (wb),
1292 					go_doc_get_state (GO_DOC (wb)));
1293 		go_doc_set_dirty (GO_DOC (wb), FALSE);
1294 	}
1295 	if (has_error || has_warning)
1296 		go_io_error_display (io_context);
1297 
1298 	g_object_unref (io_context);
1299 	g_object_unref (wb);
1300 
1301 	return !has_error;
1302 }
1303 
1304 /**
1305  * workbook_view_new_from_input:
1306  * @input: #GsfInput to read data from.
1307  * @uri: (allow-none): URI
1308  * @file_opener: (allow-none): #GOFileOpener
1309  * @io_context: (allow-none): Context to display errors.
1310  * @encoding: (allow-none): Encoding for @file_opener that understand it
1311  *
1312  * Reads @uri file using given file opener @file_opener, or probes for a valid
1313  * possibility if @file_opener is %NULL.  Reports problems to @io_context.
1314  *
1315  * Return value: (transfer full) (nullable): the newly allocated WorkbookView
1316  * or %NULL on error.
1317  **/
1318 WorkbookView *
workbook_view_new_from_input(GsfInput * input,const char * uri,GOFileOpener const * file_opener,GOIOContext * io_context,char const * encoding)1319 workbook_view_new_from_input (GsfInput *input,
1320                               const char *uri,
1321                               GOFileOpener const *file_opener,
1322                               GOIOContext *io_context,
1323                               char const *encoding)
1324 {
1325 	WorkbookView *new_wbv = NULL;
1326 
1327 	g_return_val_if_fail (GSF_IS_INPUT(input), NULL);
1328 	g_return_val_if_fail (file_opener == NULL ||
1329 			      GO_IS_FILE_OPENER (file_opener), NULL);
1330 
1331 	/* NOTE : we could support gzipped anything here if we wanted to
1332 	 * by adding a wrapper, but there is no framework for remembering that
1333 	 * the file was gzipped so let's not just yet.
1334 	 */
1335 
1336 	/* Search for an applicable opener */
1337 	if (file_opener == NULL) {
1338 		GOFileProbeLevel pl;
1339 		GList *l;
1340 		int input_refs = G_OBJECT (input)->ref_count;
1341 
1342 		for (pl = GO_FILE_PROBE_FILE_NAME; pl < GO_FILE_PROBE_LAST && file_opener == NULL; pl++) {
1343 			for (l = go_get_file_openers (); l != NULL; l = l->next) {
1344 				GOFileOpener const *tmp_fo = GO_FILE_OPENER (l->data);
1345 				int new_input_refs;
1346 				/* A name match needs to be a content match too */
1347 				if (go_file_opener_probe (tmp_fo, input, pl) &&
1348 				    (pl == GO_FILE_PROBE_CONTENT ||
1349 				     !go_file_opener_can_probe	(tmp_fo, GO_FILE_PROBE_CONTENT) ||
1350 				     go_file_opener_probe (tmp_fo, input, GO_FILE_PROBE_CONTENT)))
1351 					file_opener = tmp_fo;
1352 
1353 				new_input_refs = G_OBJECT (input)->ref_count;
1354 				if (new_input_refs != input_refs) {
1355 					g_warning ("Format %s's probe changed input ref_count from %d to %d.",
1356 						   go_file_opener_get_id (tmp_fo),
1357 						   input_refs,
1358 						   new_input_refs);
1359 					input_refs = new_input_refs;
1360 				}
1361 
1362 				if (file_opener)
1363 					break;
1364 			}
1365 		}
1366 	}
1367 
1368 	if (file_opener != NULL) {
1369 		Workbook *new_wb;
1370 		gboolean old;
1371 		GDateTime *modtime;
1372 
1373 		new_wbv = workbook_view_new (NULL);
1374 		new_wb = wb_view_get_workbook (new_wbv);
1375 		if (uri)
1376 			go_doc_set_uri (GO_DOC (new_wb), uri);
1377 
1378 		// Grab the modtime before we actually do the reading
1379 		modtime = get_uri_modtime (input, uri);
1380 		go_doc_set_modtime (GO_DOC (new_wb), modtime);
1381 		if (modtime)
1382 			g_date_time_unref (modtime);
1383 
1384 		/* disable recursive dirtying while loading */
1385 		old = workbook_enable_recursive_dirty (new_wb, FALSE);
1386 		g_object_set (new_wb, "being-loaded", TRUE, NULL);
1387 		go_file_opener_open (file_opener, encoding, io_context,
1388 		                     GO_VIEW (new_wbv), input);
1389 		g_object_set (new_wb, "being-loaded", FALSE, NULL);
1390 		workbook_enable_recursive_dirty (new_wb, old);
1391 
1392 		if (go_io_error_occurred (io_context)) {
1393 			g_object_unref (new_wb);
1394 			new_wbv = NULL;
1395 		} else if (workbook_sheet_count (new_wb) == 0) {
1396 			/* we didn't get a sheet nor an error, */
1397 			/* the user must have canceled        */
1398 			g_object_unref (new_wb);
1399 			new_wbv = NULL;
1400 		} else {
1401 			workbook_share_expressions (new_wb, TRUE);
1402 			workbook_optimize_style (new_wb);
1403 			workbook_queue_volatile_recalc (new_wb);
1404 			workbook_recalc (new_wb);
1405 			workbook_update_graphs (new_wb);
1406 			go_doc_set_saved_state
1407 				(GO_DOC (new_wb),
1408 				 go_doc_get_state (GO_DOC (new_wb)));
1409 			if (uri && workbook_get_file_exporter (new_wb))
1410 				workbook_set_last_export_uri
1411 					(new_wb, uri);
1412 		}
1413 	} else {
1414 		if (io_context) {
1415 			char *bn = go_basename_from_uri (uri);
1416 			char *errtxt = g_strdup_printf
1417 				(_("Unsupported file format for file \"%s\""),
1418 				 bn);
1419 			go_cmd_context_error_import
1420 				(GO_CMD_CONTEXT (io_context),
1421 				 errtxt);
1422 			g_free (errtxt);
1423 			g_free (bn);
1424 		}
1425 	}
1426 
1427 	return new_wbv;
1428 }
1429 
1430 /**
1431  * workbook_view_new_from_uri:
1432  * @uri: URI for file
1433  * @file_opener: (allow-none): #GOFileOpener
1434  * @io_context: Context to display errors.
1435  * @encoding: (allow-none): Encoding for @file_opener that understands it
1436  *
1437  * Reads @uri file using given file opener @file_opener, or probes for a valid
1438  * possibility if @file_opener is %NULL.  Reports problems to @io_context.
1439  *
1440  * Return value: (transfer full) (nullable): the newly allocated WorkbookView
1441  * or %NULL on error.
1442  **/
1443 WorkbookView *
workbook_view_new_from_uri(char const * uri,GOFileOpener const * file_opener,GOIOContext * io_context,char const * encoding)1444 workbook_view_new_from_uri (char const *uri,
1445 			    GOFileOpener const *file_opener,
1446 			    GOIOContext *io_context,
1447 			    char const *encoding)
1448 {
1449 	char *msg = NULL;
1450 	GError *err = NULL;
1451 	GsfInput *input;
1452 
1453 	g_return_val_if_fail (uri != NULL, NULL);
1454 
1455 	input = go_file_open (uri, &err);
1456 	if (input != NULL) {
1457 		WorkbookView *res;
1458 
1459 		res = workbook_view_new_from_input (input, uri,
1460 						    file_opener, io_context,
1461 						    encoding);
1462 		g_object_unref (input);
1463 		return res;
1464 	}
1465 
1466 	if (err != NULL) {
1467 		if (err->message != NULL)
1468 			msg = g_strdup (err->message);
1469 		g_error_free (err);
1470 	}
1471 
1472 	if (msg == NULL)
1473 		msg = g_strdup_printf (_("An unexplained error happened while opening %s"),
1474 				       uri);
1475 
1476 	if (io_context)
1477 		go_cmd_context_error_import (GO_CMD_CONTEXT (io_context), msg);
1478 	g_free (msg);
1479 
1480 	return NULL;
1481 }
1482