1 /*
2  * position.c: Utility routines for various types of positional
3  *         coordinates.
4  *
5  * Copyright (C) 2000-2005 Jody Goldberg (jody@gnome.org)
6  * Copyright (C) 2006-2009 Morten Welinder (terra@gnome.org)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 #include <gnumeric-config.h>
24 #include <gnumeric.h>
25 #include <libgnumeric.h>
26 #include <position.h>
27 
28 #include <sheet.h>
29 #include <sheet-view.h>
30 #include <cell.h>
31 #include <value.h>
32 #include <ranges.h>
33 #include <string.h>
34 #include <workbook.h>
35 
36 /* GnmCellPos made a boxed type */
37 static GnmCellPos *
gnm_cell_pos_dup(GnmCellPos * pos)38 gnm_cell_pos_dup (GnmCellPos *pos)
39 {
40 	GnmCellPos *res = g_new (GnmCellPos, 1);
41 	*res = *pos;
42 	return res;
43 }
44 
45 GType
gnm_cell_pos_get_type(void)46 gnm_cell_pos_get_type (void)
47 {
48 	static GType t = 0;
49 
50 	if (t == 0) {
51 		t = g_boxed_type_register_static ("GnmCellPos",
52 			 (GBoxedCopyFunc)gnm_cell_pos_dup,
53 			 (GBoxedFreeFunc)g_free);
54 	}
55 	return t;
56 }
57 
58 /* GnmEvalPos made a boxed type */
59 static GnmEvalPos *
gnm_eval_pos_dup(GnmEvalPos * ep)60 gnm_eval_pos_dup (GnmEvalPos *ep)
61 {
62 	return g_memdup (ep, sizeof (*ep));
63 }
64 
65 GType
gnm_eval_pos_get_type(void)66 gnm_eval_pos_get_type (void)
67 {
68 	static GType t = 0;
69 
70 	if (t == 0) {
71 		t = g_boxed_type_register_static ("GnmEvalPos",
72 			 (GBoxedCopyFunc)gnm_eval_pos_dup,
73 			 (GBoxedFreeFunc)g_free);
74 	}
75 	return t;
76 }
77 
78 /**
79  * eval_pos_init:
80  * @ep: The position to init.
81  * @s: #Sheet
82  * @col: column.
83  * @row: row
84  *
85  * Returns: (type void): the initialized #GnmEvalPos (@ep).
86  **/
87 GnmEvalPos *
eval_pos_init(GnmEvalPos * ep,Sheet * sheet,int col,int row)88 eval_pos_init (GnmEvalPos *ep, Sheet *sheet, int col, int row)
89 {
90 	g_return_val_if_fail (ep != NULL, NULL);
91 	g_return_val_if_fail (sheet != NULL, NULL);
92 
93 	ep->eval.col = col;
94 	ep->eval.row = row;
95 	ep->sheet = sheet;
96 	ep->dep = NULL;
97 	ep->array_texpr = NULL;
98 
99 	return ep;
100 }
101 
102 /**
103  * eval_pos_init_pos:
104  * @ep: The position to init.
105  * @s: #Sheet
106  * @pos: #GnmCellPos
107  *
108  * Returns: (type void): the initialized #GnmEvalPos (@ep).
109  **/
110 GnmEvalPos *
eval_pos_init_pos(GnmEvalPos * ep,Sheet * sheet,GnmCellPos const * pos)111 eval_pos_init_pos (GnmEvalPos *ep, Sheet *sheet, GnmCellPos const *pos)
112 {
113 	g_return_val_if_fail (ep != NULL, NULL);
114 	g_return_val_if_fail (sheet != NULL, NULL);
115 	g_return_val_if_fail (pos != NULL, NULL);
116 
117 	ep->eval = *pos;
118 	ep->sheet = sheet;
119 	ep->dep = NULL;
120 	ep->array_texpr = NULL;
121 
122 	return ep;
123 }
124 
125 /**
126  * eval_pos_init_dep:
127  * @ep: The position to init.
128  * @dep:
129  *
130  * Returns: (type void): the initialized #GnmEvalPos (@ep).
131  **/
132 GnmEvalPos *
eval_pos_init_dep(GnmEvalPos * ep,GnmDependent const * dep)133 eval_pos_init_dep (GnmEvalPos *ep, GnmDependent const *dep)
134 {
135 	g_return_val_if_fail (ep != NULL, NULL);
136 	g_return_val_if_fail (dep != NULL, NULL);
137 
138 	ep->eval = *dependent_pos (dep);
139 	ep->sheet = dep->sheet;
140 	ep->dep = (GnmDependent *)dep;
141 	ep->array_texpr = NULL;
142 
143 	return ep;
144 }
145 
146 /**
147  * eval_pos_init_editpos:
148  * @ep: The position to init.
149  * @sv: @Sheetview
150  *
151  * The function initializes an evalpos with the edit position from the
152  * given sheetview.
153  * Returns: (type void): the initialized #GnmEvalPos (@ep).
154  **/
155 GnmEvalPos *
eval_pos_init_editpos(GnmEvalPos * ep,SheetView const * sv)156 eval_pos_init_editpos (GnmEvalPos *ep, SheetView const *sv)
157 {
158 	g_return_val_if_fail (ep != NULL, NULL);
159 	g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
160 
161 	return eval_pos_init (ep, sv_sheet (sv),
162 		sv->edit_pos.col, sv->edit_pos.row);
163 }
164 
165 /**
166  * eval_pos_init_cell:
167  * @ep: The position to init.
168  * @cell: A cell
169  *
170  * The function initializes an evalpos with the given cell
171  *
172  * Returns: (type void): the initialized #GnmEvalPos (@ep).
173  */
174 GnmEvalPos *
eval_pos_init_cell(GnmEvalPos * ep,GnmCell const * cell)175 eval_pos_init_cell (GnmEvalPos *ep, GnmCell const *cell)
176 {
177 	g_return_val_if_fail (ep != NULL, NULL);
178 	g_return_val_if_fail (cell != NULL, NULL);
179 
180 	ep->eval = cell->pos;
181 	ep->sheet = cell->base.sheet;
182 	ep->dep = (GnmDependent *)GNM_CELL_TO_DEP (cell);
183 	ep->array_texpr = NULL;
184 
185 	return ep;
186 }
187 
188 /**
189  * eval_pos_init_sheet:
190  * @ep: The position to init.
191  * @sheet: A sheet
192  *
193  * The function initializes an evalpos with the given sheet.
194  *
195  * Returns: (type void): the initialized #GnmEvalPos (@ep).
196  */
197 GnmEvalPos *
eval_pos_init_sheet(GnmEvalPos * ep,Sheet const * sheet)198 eval_pos_init_sheet (GnmEvalPos *ep, Sheet const *sheet)
199 {
200 	g_return_val_if_fail (ep != NULL, NULL);
201 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
202 
203 	ep->eval.col = ep->eval.row = 0;
204 	ep->sheet = (Sheet *)sheet;
205 	ep->dep = NULL;
206 	ep->array_texpr = NULL;
207 
208 	return ep;
209 }
210 
211 gboolean
eval_pos_is_array_context(GnmEvalPos const * ep)212 eval_pos_is_array_context (GnmEvalPos const *ep)
213 {
214 	return ep->array_texpr != NULL;
215 }
216 
217 
218 static GnmParsePos *
gnm_parse_pos_dup(GnmParsePos * pp)219 gnm_parse_pos_dup (GnmParsePos *pp)
220 {
221 	return g_memdup (pp, sizeof (*pp));
222 }
223 
224 GType
gnm_parse_pos_get_type(void)225 gnm_parse_pos_get_type (void)
226 {
227 	static GType t = 0;
228 
229 	if (t == 0) {
230 		t = g_boxed_type_register_static ("GnmParsePos",
231 			 (GBoxedCopyFunc)gnm_parse_pos_dup,
232 			 (GBoxedFreeFunc)g_free);
233 	}
234 	return t;
235 }
236 
237 /**
238  * parse_pos_init:
239  * @pp: The position to init.
240  * @sheet: The sheet being selected
241  * @wb: The workbook being selected.
242  * @row:
243  * @col:
244  *
245  * Use either a sheet (preferred) or a workbook to initialize the supplied
246  * ParsePosition.
247  * Returns: (type void): the initialized #GnmParsePos (@pp).
248  */
249 GnmParsePos *
parse_pos_init(GnmParsePos * pp,Workbook * wb,Sheet const * sheet,int col,int row)250 parse_pos_init (GnmParsePos *pp, Workbook *wb, Sheet const *sheet,
251 		int col, int row)
252 {
253 	/* Global */
254 	if (wb == NULL && sheet == NULL)
255 		return NULL;
256 
257 	g_return_val_if_fail (pp != NULL, NULL);
258 
259 	pp->sheet = (Sheet *)sheet;
260 	pp->wb = sheet ? sheet->workbook : wb;
261 	pp->eval.col = col;
262 	pp->eval.row = row;
263 
264 	return pp;
265 }
266 
267 /**
268  * parse_pos_init_dep:
269  * @pp: The position to init.
270  * @dep: The dependent
271  *
272  * Returns: (type void): the initialized #GnmParsePos (@pp).
273  */
274 GnmParsePos *
parse_pos_init_dep(GnmParsePos * pp,GnmDependent const * dep)275 parse_pos_init_dep (GnmParsePos *pp, GnmDependent const *dep)
276 {
277 	g_return_val_if_fail (pp != NULL, NULL);
278 
279 	pp->sheet = dep->sheet;
280 	pp->wb = dep->sheet ? dep->sheet->workbook : NULL;
281 	pp->eval = *dependent_pos (dep);
282 
283 	return pp;
284 }
285 
286 /**
287  * parse_pos_init_cell:
288  * @pp: The position to init.
289  * @cell: The cell
290  *
291  * Returns: (type void): the initialized #GnmParsePos (@pp).
292  */
293 GnmParsePos *
parse_pos_init_cell(GnmParsePos * pp,GnmCell const * cell)294 parse_pos_init_cell (GnmParsePos *pp, GnmCell const *cell)
295 {
296 	g_return_val_if_fail (cell != NULL, NULL);
297 	g_return_val_if_fail (IS_SHEET (cell->base.sheet), NULL);
298 	g_return_val_if_fail (cell->base.sheet->workbook != NULL, NULL);
299 
300 	return parse_pos_init (pp, NULL, cell->base.sheet,
301 			       cell->pos.col, cell->pos.row);
302 }
303 
304 /**
305  * parse_pos_init_evalpos:
306  * @pp: The position to init.
307  * @pos: #GnmEvalPos
308  *
309  * Returns: (type void): the initialized #GnmParsePos (@pp).
310  */
311 GnmParsePos *
parse_pos_init_evalpos(GnmParsePos * pp,GnmEvalPos const * ep)312 parse_pos_init_evalpos (GnmParsePos *pp, GnmEvalPos const *ep)
313 {
314 	g_return_val_if_fail (ep != NULL, NULL);
315 
316 	return parse_pos_init (pp, NULL, ep->sheet, ep->eval.col, ep->eval.row);
317 }
318 
319 /**
320  * parse_pos_init_editpos:
321  * @pp: The position to init.
322  * @sv: sheet view
323  *
324  * Returns: (type void): the initialized #GnmParsePos (@pp).
325  */
326 GnmParsePos *
parse_pos_init_editpos(GnmParsePos * pp,SheetView const * sv)327 parse_pos_init_editpos (GnmParsePos *pp, SheetView const *sv)
328 {
329 	g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
330 
331 	return parse_pos_init (pp, NULL, sv_sheet (sv),
332 		sv->edit_pos.col, sv->edit_pos.row);
333 }
334 
335 /**
336  * parse_pos_init_sheet:
337  * @pp: The position to init.
338  * @sheet: The sheet
339  *
340  * Returns: (type void): the initialized #GnmParsePos (@pp).
341  */
342 GnmParsePos *
parse_pos_init_sheet(GnmParsePos * pp,Sheet const * sheet)343 parse_pos_init_sheet (GnmParsePos *pp, Sheet const *sheet)
344 {
345 	g_return_val_if_fail (pp != NULL, NULL);
346 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
347 	return parse_pos_init (pp, NULL, sheet, 0, 0);
348 }
349 
350 /********************************************************************************/
351 
352 
353 static GnmCellRef *
gnm_cellref_dup(GnmCellRef * cr)354 gnm_cellref_dup (GnmCellRef *cr)
355 {
356 	return g_memdup (cr, sizeof (*cr));
357 }
358 
359 GType
gnm_cellref_get_type(void)360 gnm_cellref_get_type (void)
361 {
362 	static GType t = 0;
363 
364 	if (t == 0) {
365 		t = g_boxed_type_register_static ("GnmCellRef",
366 			 (GBoxedCopyFunc)gnm_cellref_dup,
367 			 (GBoxedFreeFunc)g_free);
368 	}
369 	return t;
370 }
371 GnmCellRef *
gnm_cellref_init(GnmCellRef * ref,Sheet * sheet,int col,int row,gboolean relative)372 gnm_cellref_init (GnmCellRef *ref, Sheet *sheet, int col, int row, gboolean relative)
373 {
374 	ref->sheet = sheet;
375 	ref->col   = col;
376 	ref->row   = row;
377 	ref->col_relative = ref->row_relative = relative;
378 
379 	return ref;
380 }
381 
382 gboolean
gnm_cellref_equal(GnmCellRef const * a,GnmCellRef const * b)383 gnm_cellref_equal (GnmCellRef const *a, GnmCellRef const *b)
384 {
385 	return (a->col == b->col) &&
386 	       (a->col_relative == b->col_relative) &&
387 	       (a->row == b->row) &&
388 	       (a->row_relative == b->row_relative) &&
389 	       (a->sheet == b->sheet);
390 }
391 
392 guint
gnm_cellref_hash(GnmCellRef const * cr)393 gnm_cellref_hash (GnmCellRef const *cr)
394 {
395 	guint h = cr->row;
396 	h = (h << 16) | (h >> 16);
397 	h ^= ((guint)cr->col << 2);
398 	if (cr->col_relative) h ^= 1;
399 	if (cr->row_relative) h ^= 2;
400 	return h;
401 }
402 
403 int
gnm_cellref_get_col(GnmCellRef const * ref,GnmEvalPos const * ep)404 gnm_cellref_get_col (GnmCellRef const *ref, GnmEvalPos const *ep)
405 {
406 	g_return_val_if_fail (ref != NULL, 0);
407 	g_return_val_if_fail (ep != NULL, 0);
408 
409 	if (ref->col_relative) {
410 		Sheet const *sheet = eval_sheet (ref->sheet, ep->sheet);
411 		int res = (ep->eval.col + ref->col) % gnm_sheet_get_max_cols (sheet);
412 		if (res < 0)
413 			return res + gnm_sheet_get_max_cols (sheet);
414 		return res;
415 	}
416 	return ref->col;
417 }
418 
419 int
gnm_cellref_get_row(GnmCellRef const * ref,GnmEvalPos const * ep)420 gnm_cellref_get_row (GnmCellRef const *ref, GnmEvalPos const *ep)
421 {
422 	g_return_val_if_fail (ref != NULL, 0);
423 	g_return_val_if_fail (ep != NULL, 0);
424 
425 	if (ref->row_relative) {
426 		Sheet const *sheet = eval_sheet (ref->sheet, ep->sheet);
427 		int res = (ep->eval.row + ref->row) % gnm_sheet_get_max_rows (sheet);
428 		if (res < 0)
429 			return res + gnm_sheet_get_max_rows (sheet);
430 		return res;
431 	}
432 	return ref->row;
433 }
434 
435 static int
modulo(int i,int max)436 modulo (int i, int max)
437 {
438 	if (i < 0) {
439 		i %= max;
440 		if (i < 0)
441 			i += max;
442 	} else if (i >= max)
443 		i %= max;
444 
445 	return i;
446 }
447 
448 
449 void
gnm_cellpos_init_cellref_ss(GnmCellPos * res,GnmCellRef const * cell_ref,GnmCellPos const * pos,GnmSheetSize const * ss)450 gnm_cellpos_init_cellref_ss (GnmCellPos *res, GnmCellRef const *cell_ref,
451 			     GnmCellPos const *pos, GnmSheetSize const *ss)
452 {
453 	g_return_if_fail (cell_ref != NULL);
454 	g_return_if_fail (res != NULL);
455 
456 	if (cell_ref->col_relative) {
457 		int col = cell_ref->col + pos->col;
458 		res->col = modulo (col, ss->max_cols);
459 	} else
460 		res->col = cell_ref->col;
461 
462 	if (cell_ref->row_relative) {
463 		int row = cell_ref->row + pos->row;
464 		res->row = modulo (row, ss->max_rows);
465 	} else
466 		res->row = cell_ref->row;
467 }
468 
469 void
gnm_cellpos_init_cellref(GnmCellPos * res,GnmCellRef const * cell_ref,GnmCellPos const * pos,Sheet const * base_sheet)470 gnm_cellpos_init_cellref (GnmCellPos *res, GnmCellRef const *cell_ref,
471 			  GnmCellPos const *pos, Sheet const *base_sheet)
472 {
473 	Sheet const *sheet = eval_sheet (cell_ref->sheet, base_sheet);
474 	gnm_cellpos_init_cellref_ss (res, cell_ref, pos,
475 				     gnm_sheet_get_size (sheet));
476 }
477 
478 void
gnm_cellref_make_abs(GnmCellRef * dest,GnmCellRef const * src,GnmEvalPos const * ep)479 gnm_cellref_make_abs (GnmCellRef *dest, GnmCellRef const *src, GnmEvalPos const *ep)
480 {
481 	GnmCellPos pos;
482 
483 	g_return_if_fail (dest != NULL);
484 	g_return_if_fail (src != NULL);
485 	g_return_if_fail (ep != NULL);
486 
487 	gnm_cellpos_init_cellref (&pos, src, &ep->eval, ep->sheet);
488 
489 	dest->sheet = src->sheet;
490 	dest->col = pos.col;
491 	dest->row = pos.row;
492 	dest->col_relative = FALSE;
493 	dest->row_relative = FALSE;
494 }
495 
496 void
gnm_cellref_set_col_ar(GnmCellRef * cr,GnmParsePos const * pp,gboolean abs_rel)497 gnm_cellref_set_col_ar (GnmCellRef *cr, GnmParsePos const *pp, gboolean abs_rel)
498 {
499 	if (cr->col_relative ^ abs_rel) {
500 		if (cr->col_relative)
501 			cr->col += pp->eval.col;
502 		else
503 			cr->col -= pp->eval.col;
504 		cr->col_relative = abs_rel;
505 	}
506 }
507 
508 void
gnm_cellref_set_row_ar(GnmCellRef * cr,GnmParsePos const * pp,gboolean abs_rel)509 gnm_cellref_set_row_ar (GnmCellRef *cr, GnmParsePos const *pp, gboolean abs_rel)
510 {
511 	if (cr->row_relative ^ abs_rel) {
512 		if (cr->row_relative)
513 			cr->row += pp->eval.row;
514 		else
515 			cr->row -= pp->eval.row;
516 		cr->row_relative = abs_rel;
517 	}
518 }
519 
520 gboolean
gnm_rangeref_equal(GnmRangeRef const * a,GnmRangeRef const * b)521 gnm_rangeref_equal (GnmRangeRef const *a, GnmRangeRef const *b)
522 {
523 	return  gnm_cellref_equal (&a->a, &b->a) &&
524 		gnm_cellref_equal (&a->b, &b->b);
525 }
526 
527 guint
gnm_rangeref_hash(GnmRangeRef const * rr)528 gnm_rangeref_hash (GnmRangeRef const *rr)
529 {
530 	guint h = gnm_cellref_hash (&rr->a);
531 	h = (h << 16) | (h >> 16);
532 	h ^= gnm_cellref_hash (&rr->b);
533 	return h;
534 }
535 
536 GnmRangeRef *
gnm_rangeref_dup(GnmRangeRef const * rr)537 gnm_rangeref_dup (GnmRangeRef const *rr)
538 {
539 	GnmRangeRef *res;
540 
541 	g_return_val_if_fail (rr != NULL, NULL);
542 
543 	res = g_new (GnmRangeRef, 1);
544 	*res = *rr;
545 	return res;
546 }
547 
548 GType
gnm_rangeref_get_type(void)549 gnm_rangeref_get_type (void)
550 {
551 	static GType t = 0;
552 
553 	if (t == 0) {
554 		t = g_boxed_type_register_static ("GnmRangeRef",
555 			 (GBoxedCopyFunc)gnm_rangeref_dup,
556 			 (GBoxedFreeFunc)g_free);
557 	}
558 	return t;
559 }
560 
561 void
gnm_rangeref_normalize_pp(GnmRangeRef const * ref,GnmParsePos const * pp,Sheet ** start_sheet,Sheet ** end_sheet,GnmRange * dest)562 gnm_rangeref_normalize_pp (GnmRangeRef const *ref, GnmParsePos const *pp,
563 			   Sheet **start_sheet, Sheet **end_sheet,
564 			   GnmRange *dest)
565 {
566 	GnmSheetSize const *ss;
567 
568 	g_return_if_fail (ref != NULL);
569 	g_return_if_fail (pp != NULL);
570 
571 	*start_sheet = eval_sheet (ref->a.sheet, pp->sheet);
572 	*end_sheet   = eval_sheet (ref->b.sheet, *start_sheet);
573 
574 	ss = gnm_sheet_get_size2 (*start_sheet, pp->wb);
575 	gnm_cellpos_init_cellref_ss (&dest->start, &ref->a, &pp->eval, ss);
576 
577 	ss = *end_sheet
578 		? gnm_sheet_get_size (*end_sheet)
579 		: ss;
580 	gnm_cellpos_init_cellref_ss (&dest->end, &ref->b, &pp->eval, ss);
581 
582 	range_normalize (dest);
583 }
584 
585 /**
586  * gnm_rangeref_normalize:
587  *
588  * Take a range_ref and normalize it by converting to absolute coords and
589  * handling inversions.
590  */
591 void
gnm_rangeref_normalize(GnmRangeRef const * ref,GnmEvalPos const * ep,Sheet ** start_sheet,Sheet ** end_sheet,GnmRange * dest)592 gnm_rangeref_normalize (GnmRangeRef const *ref, GnmEvalPos const *ep,
593 			Sheet **start_sheet, Sheet **end_sheet, GnmRange *dest)
594 {
595 	GnmParsePos pp;
596 
597 	parse_pos_init_evalpos (&pp, ep);
598 	gnm_rangeref_normalize_pp (ref, &pp, start_sheet, end_sheet, dest);
599 }
600 
601 guint
gnm_cellpos_hash(GnmCellPos const * key)602 gnm_cellpos_hash (GnmCellPos const *key)
603 {
604 	guint h = key->row;
605 	h = (h << 16) | (h >> 16);
606 	h ^= key->col;
607 	return h;
608 }
609 
610 gint
gnm_cellpos_equal(GnmCellPos const * a,GnmCellPos const * b)611 gnm_cellpos_equal (GnmCellPos const *a, GnmCellPos const *b)
612 {
613 	return (a->row == b->row && a->col == b->col);
614 }
615