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