1 
2 /*
3  * workbook-control.c: The base class for the displaying a workbook.
4  *
5  * Copyright (C) 2000-2002 Jody Goldberg (jody@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) version 3.
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 
23 #include <gnumeric-config.h>
24 #include <gnm-i18n.h>
25 #include <gnumeric.h>
26 #include <workbook-control-priv.h>
27 
28 #include <application.h>
29 #include <workbook-view.h>
30 #include <workbook-priv.h>
31 #include <sheet.h>
32 #include <sheet-view.h>
33 #include <sheet-utils.h>
34 #include <selection.h>
35 #include <commands.h>
36 #include <value.h>
37 #include <ranges.h>
38 #include <expr-name.h>
39 #include <expr.h>
40 #include <command-context.h>
41 #include <goffice/goffice.h>
42 #include <gsf/gsf-impl-utils.h>
43 
44 enum {
45 	PROP_0,
46 	PROP_VIEW
47 };
48 
49 #define WBC_CLASS(o) GNM_WBC_CLASS (G_OBJECT_GET_CLASS (o))
50 #define WBC_VIRTUAL_FULL(func, handle, arglist, call)		\
51 void wb_control_ ## func arglist				\
52 {								\
53 	WorkbookControlClass *wbc_class = WBC_CLASS (wbc);	\
54 								\
55 	g_return_if_fail (wbc_class != NULL);			\
56 								\
57 	if (wbc_class != NULL && wbc_class->handle != NULL)	\
58 		wbc_class->handle call;				\
59 }
60 #define WBC_VIRTUAL(func, arglist, call) WBC_VIRTUAL_FULL(func, func, arglist, call)
61 
62 /**
63  * workbook_control_new_wrapper:
64  * @wbc: #WorkbookControl
65  * @wbv: #WorkbookView
66  * @wb: #Workbook
67  * @extra: (allow-none):
68  *
69  * Returns: (transfer full): the newly allocated #WorkbookControl.
70  **/
71 WorkbookControl *
workbook_control_new_wrapper(WorkbookControl * wbc,WorkbookView * wbv,Workbook * wb,void * extra)72 workbook_control_new_wrapper (WorkbookControl *wbc, WorkbookView *wbv, Workbook *wb,
73 			void *extra)
74 {
75 	WorkbookControlClass *wbc_class = WBC_CLASS (wbc);
76 
77 	g_return_val_if_fail (wbc_class != NULL, NULL);
78 
79 	if (wbc_class != NULL && wbc_class->control_new != NULL)
80 		return wbc_class->control_new (wbc, wbv, wb, extra);
81 	return NULL;
82 }
83 
84 WBC_VIRTUAL (style_feedback,
85 	(WorkbookControl *wbc, GnmStyle const *changes), (wbc, changes))
86 WBC_VIRTUAL (edit_line_set,
87 	(WorkbookControl *wbc, char const *text), (wbc, text))
88 WBC_VIRTUAL (selection_descr_set,
89 	(WorkbookControl *wbc, char const *text), (wbc, text))
90 
91 WBC_VIRTUAL_FULL (sheet_remove, sheet.remove,
92 	(WorkbookControl *wbc, Sheet *sheet), (wbc, sheet))
93 WBC_VIRTUAL_FULL (sheet_focus, sheet.focus,
94 	(WorkbookControl *wbc, Sheet *sheet), (wbc, sheet))
95 WBC_VIRTUAL_FULL (sheet_remove_all, sheet.remove_all,
96 	(WorkbookControl *wbc), (wbc))
97 
98 WBC_VIRTUAL_FULL (undo_redo_truncate, undo_redo.truncate,
99 	(WorkbookControl *wbc, int n, gboolean is_undo), (wbc, n, is_undo))
100 WBC_VIRTUAL_FULL (undo_redo_pop, undo_redo.pop,
101 	(WorkbookControl *wbc, gboolean is_undo), (wbc, is_undo))
102 WBC_VIRTUAL_FULL (undo_redo_push, undo_redo.push,
103 	(WorkbookControl *wbc, gboolean is_undo, char const *text, gpointer key),
104 	(wbc, is_undo, text, key))
105 WBC_VIRTUAL_FULL (undo_redo_labels, undo_redo.labels,
106 	(WorkbookControl *wbc, char const *undo, char const *redo),
107 	(wbc, undo, redo))
108 
109 WBC_VIRTUAL_FULL (menu_state_update, menu_state.update,
110         (WorkbookControl *wbc, int flags),
111 	(wbc, flags))
112 
113 WBC_VIRTUAL (paste_from_selection,
114 	(WorkbookControl *wbc, GnmPasteTarget const *pt), (wbc, pt))
115 WBC_VIRTUAL (update_action_sensitivity,
116 	(WorkbookControl *wbc), (wbc))
117 
118 void
wb_control_sheet_add(WorkbookControl * wbc,SheetView * sv)119 wb_control_sheet_add (WorkbookControl *wbc, SheetView *sv)
120 {
121 	WorkbookControlClass *wbc_class;
122 
123 	g_return_if_fail (GNM_IS_WBC (wbc));
124 
125 	wbc_class = WBC_CLASS (wbc);
126 	if (wbc_class != NULL && wbc_class->sheet.add != NULL) {
127 		Sheet *new_sheet = sv_sheet (sv);
128 
129 		wbc_class->sheet.add (wbc, sv);
130 
131 		/* If this is the current sheet init the display */
132 		if (new_sheet == wb_control_cur_sheet (wbc)) {
133 			WorkbookView *wbv = wb_control_view (wbc);
134 			wb_control_sheet_focus (wbc, new_sheet);
135 			wb_view_selection_desc (wbv, TRUE, wbc);
136 			wb_view_edit_line_set (wbv, wbc);
137 			wb_control_style_feedback (wbc, NULL);
138 			wb_control_menu_state_update (wbc, MS_ALL);
139 			wb_control_update_action_sensitivity (wbc);
140 		}
141 	}
142 }
143 
144 gboolean
wb_control_claim_selection(WorkbookControl * wbc)145 wb_control_claim_selection (WorkbookControl *wbc)
146 {
147 	WorkbookControlClass *wbc_class;
148 
149 	g_return_val_if_fail (GNM_IS_WBC (wbc), FALSE);
150 
151 	wbc_class = WBC_CLASS (wbc);
152 	if (wbc_class != NULL && wbc_class->claim_selection != NULL)
153 		return wbc_class->claim_selection (wbc);
154 	return TRUE; /* no handler means we always get the selection */
155 }
156 
157 /**
158  * wb_control_validation_msg:
159  *	 1 : ignore invalid and accept result
160  *	 0 : discard invalid and finish editing
161  *	-1 : continue editing
162  **/
163 int
wb_control_validation_msg(WorkbookControl * wbc,ValidationStyle v,char const * title,char const * msg)164 wb_control_validation_msg (WorkbookControl *wbc, ValidationStyle v,
165 			   char const *title, char const *msg)
166 {
167 	WorkbookControlClass *wbc_class;
168 
169 	g_return_val_if_fail (GNM_IS_WBC (wbc), 1);
170 
171 	wbc_class = WBC_CLASS (wbc);
172 	if (wbc_class != NULL && wbc_class->validation_msg != NULL)
173 		return wbc_class->validation_msg (wbc, v, title, msg);
174 	return 1; /* no handler, always accept */
175 }
176 
177 /**
178  * wb_control_view:
179  * @wbc: #WorkbookControl
180  *
181  * Returns: (transfer none): the workbook view.
182  **/
183 WorkbookView *
wb_control_view(WorkbookControl const * wbc)184 wb_control_view (WorkbookControl const *wbc)
185 {
186 	g_return_val_if_fail (GNM_IS_WBC (wbc), NULL);
187 	return wbc->wb_view;
188 }
189 
190 /**
191  * wb_control_get_doc:
192  * @wbc: #WorkbookControl
193  *
194  * Returns: (transfer none): the workbook set as a #GODoc.
195  **/
196 GODoc *
wb_control_get_doc(WorkbookControl const * wbc)197 wb_control_get_doc (WorkbookControl const *wbc)
198 {
199 	return GO_DOC (wb_control_get_workbook (wbc));
200 }
201 
202 /**
203  * wb_control_get_workbook:
204  * @wbc: #WorkbookControl
205  *
206  * Returns: (transfer none): the workbook.
207  **/
208 Workbook *
wb_control_get_workbook(WorkbookControl const * wbc)209 wb_control_get_workbook (WorkbookControl const *wbc)
210 {
211 	g_return_val_if_fail (GNM_IS_WBC (wbc), NULL);
212 	return wbc->wb_view ? wb_view_get_workbook (wbc->wb_view) : NULL;
213 }
214 
215 /**
216  * wb_control_cur_sheet:
217  * @wbc: #WorkbookControl
218  *
219  * Returns: (transfer none): the current sheet.
220  **/
221 Sheet *
wb_control_cur_sheet(WorkbookControl const * wbc)222 wb_control_cur_sheet (WorkbookControl const *wbc)
223 {
224 	g_return_val_if_fail (GNM_IS_WBC (wbc), NULL);
225 	return wb_view_cur_sheet (wbc->wb_view);
226 }
227 
228 /**
229  * wb_control_cur_sheet_view:
230  * @wbc: #WorkbookControl
231  *
232  * Returns: (transfer none): the current sheet view.
233  **/
234 SheetView *
wb_control_cur_sheet_view(WorkbookControl const * wbc)235 wb_control_cur_sheet_view (WorkbookControl const *wbc)
236 {
237 	g_return_val_if_fail (GNM_IS_WBC (wbc), NULL);
238 	return wb_view_cur_sheet_view (wbc->wb_view);
239 }
240 
241 static void
wb_create_name(WorkbookControl * wbc,char const * text,GnmParsePos * pp)242 wb_create_name (WorkbookControl *wbc, char const *text, GnmParsePos *pp)
243 {
244 	GnmRange const *r;
245 	GnmCellRef a, b;
246 	GnmExpr const *target_range;
247 
248 	r = selection_first_range (wb_control_cur_sheet_view (wbc),
249 		                   GO_CMD_CONTEXT (wbc), _("Define Name"));
250 	if (r != NULL) {
251 		a.sheet = b.sheet = wb_control_cur_sheet (wbc);
252 		a.col = r->start.col;
253 		a.row = r->start.row;
254 		b.col = r->end.col;
255 		b.row = r->end.row;
256 		a.col_relative = a.row_relative = b.col_relative = b.row_relative = FALSE;
257 		pp->sheet = NULL; /* make it a global name */
258 		if (gnm_cellref_equal (&a, &b))
259 			target_range = gnm_expr_new_cellref (&a);
260 		else
261 			target_range = gnm_expr_new_constant (
262 				value_new_cellrange_unsafe (&a, &b));
263 		cmd_define_name (wbc, text, pp, gnm_expr_top_new (target_range), NULL);
264 	}
265 }
266 
267 /*
268  * Select the given range and make the it visible.
269  */
270 gboolean
wb_control_jump(WorkbookControl * wbc,Sheet * sheet,const GnmRangeRef * r)271 wb_control_jump (WorkbookControl *wbc, Sheet *sheet, const GnmRangeRef *r)
272 {
273 	SheetView *sv;
274 	GnmCellPos tmp;
275 
276 	if (r->a.sheet)
277 		sheet = r->a.sheet;
278 
279 	if (!sheet_is_visible (sheet)) {
280 		go_cmd_context_error_invalid
281 			(GO_CMD_CONTEXT (wbc),
282 			 _("Cannot jump to an invisible sheet"),
283 			 sheet->name_unquoted);
284 		return FALSE;
285 	}
286 
287 	sv = sheet_get_view (sheet, wb_control_view (wbc));
288 
289 	tmp.col = r->a.col;
290 	tmp.row = r->a.row;
291 	sv_selection_set (sv, &tmp, r->a.col, r->a.row, r->b.col, r->b.row);
292 	gnm_sheet_view_make_cell_visible (sv, r->b.col, r->b.row, FALSE);
293 	gnm_sheet_view_make_cell_visible (sv, r->a.col, r->a.row, FALSE);
294 	gnm_sheet_view_update (sv);
295 	if (wb_control_cur_sheet (wbc) != sheet)
296 		wb_view_sheet_focus (wbc->wb_view, sheet);
297 
298 	return TRUE;
299 }
300 
301 /*
302  * This is called when something is entered in the location entry.
303  * We either go there (if the text refers to a cell by address or
304  * name), or we try to define a name for the selection.
305  */
306 gboolean
wb_control_parse_and_jump(WorkbookControl * wbc,char const * text)307 wb_control_parse_and_jump (WorkbookControl *wbc, char const *text)
308 {
309 	Sheet *sheet = wb_control_cur_sheet (wbc);
310 	GnmParsePos pp;
311 	GnmEvalPos ep;
312 	GnmValue *target;
313 	GnmRangeRef range;
314 	SheetView *sv;
315 
316 	if (text == NULL || *text == '\0')
317 		return FALSE;
318 
319 	sv = wb_control_cur_sheet_view (wbc);
320 	parse_pos_init_editpos (&pp, sv);
321 	target = value_new_cellrange_parsepos_str (&pp, text,
322 						   GNM_EXPR_PARSE_DEFAULT);
323 	if (target == NULL) {
324 		GnmExprTop const *texpr;
325 
326 		texpr = gnm_expr_parse_str
327 			(text, &pp, GNM_EXPR_PARSE_DEFAULT,
328 			 gnm_conventions_xls_r1c1, NULL);
329 		if (texpr != NULL)  {
330 			target = gnm_expr_top_get_range (texpr);
331 			gnm_expr_top_unref (texpr);
332 		}
333 	}
334 	if (target == NULL) {
335 		GnmExprTop const *texpr;
336 
337 		texpr = gnm_expr_parse_str
338 			(text, &pp, GNM_EXPR_PARSE_DEFAULT,
339 			 gnm_conventions_default, NULL);
340 		if (texpr != NULL)  {
341 			target = gnm_expr_top_get_range (texpr);
342 			gnm_expr_top_unref (texpr);
343 		}
344 	}
345 
346 	if (target == NULL) {
347 		/* Not an address; is it a name? */
348 		GnmParsePos pp;
349 		GnmNamedExpr *nexpr = expr_name_lookup (
350 			parse_pos_init_sheet (&pp, sheet), text);
351 
352 		/* If no name, or just a placeholder exists create a name */
353 		if (nexpr == NULL || expr_name_is_placeholder (nexpr)) {
354 			wb_create_name (wbc, text, &pp);
355 			return FALSE;
356 		} else {
357 			target = gnm_expr_top_get_range (nexpr->texpr);
358 			if (target == NULL) {
359 				go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
360 					_("Address"), text);
361 				return FALSE;
362 			}
363 		}
364 	}
365 
366 	eval_pos_init_editpos (&ep, sv);
367 	gnm_cellref_make_abs (&range.a, &target->v_range.cell.a, &ep);
368 	gnm_cellref_make_abs (&range.b, &target->v_range.cell.b, &ep);
369 	value_release (target);
370 
371 	return wb_control_jump (wbc, sheet, &range);
372 }
373 
374 void
wb_control_navigate_to_cell(WorkbookControl * wbc,wb_control_navigator_t to)375 wb_control_navigate_to_cell (WorkbookControl *wbc, wb_control_navigator_t to)
376 {
377 	Sheet *sheet = wb_control_cur_sheet (wbc);
378 	SheetView *sv = wb_control_cur_sheet_view (wbc);
379 	GnmRange region;
380 	GnmRange const *first = selection_first_range (sv, NULL, NULL);
381 	GnmRangeRef rangeref;
382 
383 	region = *first;
384 	gnm_sheet_guess_data_range (sheet, &region);
385 	range_ensure_sanity (&region, sheet);
386 
387 	switch (to) {
388 	case navigator_top:
389 		region.end.row = region.start.row;
390 		region.start.col = first->start.col;
391 		region.end.col = first->end.col;
392 		break;
393 	case navigator_bottom:
394 		region.start.row = region.end.row;
395 		region.start.col = first->start.col;
396 		region.end.col = first->end.col;
397 		break;
398 	case navigator_first:
399 		region.end.col = region.start.col;
400 		region.start.row = first->start.row;
401 		region.end.row = first->end.row;
402 		break;
403 	case navigator_last:
404 		region.start.col = region.end.col;
405 		region.start.row = first->start.row;
406 		region.end.row = first->end.row;
407 		break;
408 	default:
409 		break;
410 	}
411 	gnm_cellref_init (&rangeref.a, sheet,
412 			  region.start.col, region.start.row, FALSE);
413 	gnm_cellref_init (&rangeref.b, sheet,
414 			  region.end.col, region.end.row, FALSE);
415 
416 	wb_control_jump (wbc, sheet, &rangeref);
417 
418 	return;
419 }
420 
421 
422 
423 
424 static void
cb_wbc_clipboard_modified(G_GNUC_UNUSED GnmApp * app,WorkbookControl * wbc)425 cb_wbc_clipboard_modified (G_GNUC_UNUSED GnmApp *app, WorkbookControl *wbc)
426 {
427 	wb_control_menu_state_update (wbc, MS_PASTE_SPECIAL);
428 }
429 
430 /*****************************************************************************/
431 
432 static void
wbc_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)433 wbc_get_property (GObject     *object,
434 		  guint        property_id,
435 		  GValue      *value,
436 		  GParamSpec  *pspec)
437 {
438 	WorkbookControl *wbc = (WorkbookControl *)object;
439 
440 	switch (property_id) {
441 	case PROP_VIEW:
442 		g_value_set_object (value, wbc->wb_view);
443 		break;
444 	default:
445 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
446 		break;
447 	}
448 }
449 
450 static void
wbc_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)451 wbc_set_property (GObject      *object,
452 		  guint         property_id,
453 		  GValue const *value,
454 		  GParamSpec   *pspec)
455 {
456 	WorkbookControl *wbc = (WorkbookControl *)object;
457 
458 	switch (property_id) {
459 	case PROP_VIEW:
460 		wbc->wb_view = g_value_get_object (value);
461 		break;
462 	default:
463 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
464 		break;
465 	}
466 }
467 
468 static GObjectClass *parent_klass;
469 
470 static void
wbc_dispose(GObject * obj)471 wbc_dispose (GObject *obj)
472 {
473 	WorkbookControl *wbc = GNM_WBC (obj);
474 	if (wbc->clipboard_changed_signal) {
475 		g_signal_handler_disconnect (gnm_app_get_app (),
476 					     wbc->clipboard_changed_signal);
477 		wbc->clipboard_changed_signal = 0;
478 	}
479 
480 	if (wbc->wb_view != NULL)
481 		wb_view_detach_control (wbc);
482 
483 	parent_klass->dispose (obj);
484 }
485 
486 static void
workbook_control_class_init(GObjectClass * object_class)487 workbook_control_class_init (GObjectClass *object_class)
488 {
489 	parent_klass = g_type_class_peek_parent (object_class);
490 	object_class->dispose = wbc_dispose;
491 	object_class->get_property = wbc_get_property;
492 	object_class->set_property = wbc_set_property;
493 
494 	g_object_class_install_property
495 		(object_class,
496 		 PROP_VIEW,
497 		 g_param_spec_object ("view",
498 				      P_("View"),
499 				      P_("The workbook view being controlled."),
500 				      GNM_WORKBOOK_VIEW_TYPE,
501 				      GSF_PARAM_STATIC |
502 				      G_PARAM_READWRITE));
503 }
504 
505 static void
workbook_control_init(GObject * obj)506 workbook_control_init (GObject *obj)
507 {
508 	WorkbookControl *wbc = GNM_WBC (obj);
509 
510 	wbc->clipboard_changed_signal = g_signal_connect (
511 		gnm_app_get_app (),
512 		"clipboard_modified",
513 		G_CALLBACK (cb_wbc_clipboard_modified), wbc);
514 }
515 
516 static void
wbc_cmd_context_init(G_GNUC_UNUSED GOCmdContextClass * iface)517 wbc_cmd_context_init (G_GNUC_UNUSED GOCmdContextClass *iface)
518 {
519 #if 0
520 	iface->get_password	    = ;
521 	iface->set_sensitive	    = ;
522 	iface->error.error	    = ;
523 	iface->error.error_info	    = ;
524 	iface->error.error_info_list= ;
525 	iface->progress_set	    = ;
526 	iface->progress_message_set = ;
527 #endif
528 }
529 
530 GSF_CLASS_FULL (WorkbookControl, workbook_control, NULL, NULL,
531 		workbook_control_class_init, NULL, workbook_control_init,
532 		GO_TYPE_DOC_CONTROL, 0,
533 		GSF_INTERFACE (wbc_cmd_context_init, GO_TYPE_CMD_CONTEXT))
534 
535 
536 void
wb_control_set_view(WorkbookControl * wbc,WorkbookView * opt_view,Workbook * opt_wb)537 wb_control_set_view (WorkbookControl *wbc,
538 		     WorkbookView *opt_view, Workbook *opt_wb)
539 {
540 	WorkbookView *wbv;
541 
542 	g_return_if_fail (GNM_IS_WBC (wbc));
543 	g_return_if_fail (wbc->wb_view == NULL);
544 
545 	wbv = (opt_view != NULL) ? opt_view : workbook_view_new (opt_wb);
546 	wb_view_attach_control (wbv, wbc);
547 	go_doc_control_set_doc (GO_DOC_CONTROL (wbc), GO_DOC (wb_view_get_workbook (wbv)));
548 }
549 
550 void
wb_control_init_state(WorkbookControl * wbc)551 wb_control_init_state (WorkbookControl *wbc)
552 {
553 	WorkbookView *wbv;
554 	WorkbookControlClass *wbc_class;
555 
556 	g_return_if_fail (GNM_IS_WBC (wbc));
557 
558 	/* Setup the undo/redo combos */
559 	command_setup_combos (wbc);
560 
561 	/* Add views for all existing sheets */
562 	wbv = wb_control_view (wbc);
563 	WORKBOOK_FOREACH_SHEET(wb_control_get_workbook (wbc), sheet, {
564 		SHEET_FOREACH_VIEW (sheet, view, {
565 			if (sv_wbv (view) == wbv)
566 				wb_control_sheet_add (wbc, view);
567 		});
568 	});
569 
570 	wbc_class = WBC_CLASS (wbc);
571 	if (wbc_class != NULL && wbc_class->init_state != NULL)
572 		wbc_class->init_state (wbc);
573 }
574