position.c 13.3 KB
Newer Older
1 2 3 4
/*
 * position.c: Utility routines for various types of positional
 *         coordinates.
 *
5
 * Copyright (C) 2000-2005 Jody Goldberg (jody@gnome.org)
Morten Welinder's avatar
Morten Welinder committed
6
 * Copyright (C) 2006-2009 Morten Welinder (terra@gnome.org)
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21 22
 * USA
 */
23
#include <gnumeric-config.h>
24 25 26
#include <gnumeric.h>
#include <libgnumeric.h>
#include <position.h>
27

28 29 30 31 32
#include <sheet.h>
#include <sheet-view.h>
#include <cell.h>
#include <value.h>
#include <ranges.h>
33
#include <string.h>
34
#include <workbook.h>
35

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
/* GnmCellPos made a boxed type */
static GnmCellPos *
gnm_cell_pos_dup (GnmCellPos *pos)
{
	GnmCellPos *res = g_new (GnmCellPos, 1);
	*res = *pos;
	return res;
}

GType
gnm_cell_pos_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmCellPos",
			 (GBoxedCopyFunc)gnm_cell_pos_dup,
			 (GBoxedFreeFunc)g_free);
	}
	return t;
}

/* GnmEvalPos made a boxed type */
static GnmEvalPos *
gnm_eval_pos_dup (GnmEvalPos *ep)
{
62
	return g_memdup (ep, sizeof (*ep));
63 64 65 66 67 68 69 70 71 72 73 74 75 76
}

GType
gnm_eval_pos_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmEvalPos",
			 (GBoxedCopyFunc)gnm_eval_pos_dup,
			 (GBoxedFreeFunc)g_free);
	}
	return t;
}
Jukka Pekka's avatar
Jukka Pekka committed
77

78 79
/**
 * eval_pos_init:
80
 * @ep: The position to init.
81 82 83 84
 * @s: #Sheet
 * @col: column.
 * @row: row
 *
Morten Welinder's avatar
Morten Welinder committed
85
 * Returns: (skip) (transfer none): the initialized #GnmEvalPos (@ep).
86
 **/
Morten Welinder's avatar
Morten Welinder committed
87
GnmEvalPos *
88
eval_pos_init (GnmEvalPos *ep, Sheet *sheet, int col, int row)
89
{
90
	g_return_val_if_fail (ep != NULL, NULL);
91 92
	g_return_val_if_fail (sheet != NULL, NULL);

93 94
	ep->eval.col = col;
	ep->eval.row = row;
95
	ep->sheet = sheet;
Morten Welinder's avatar
Morten Welinder committed
96 97
	ep->dep = NULL;
	ep->array_texpr = NULL;
98 99 100 101

	return ep;
}

102 103
/**
 * eval_pos_init_pos:
104
 * @ep: The position to init.
105 106 107
 * @s: #Sheet
 * @pos: #GnmCellPos
 *
Morten Welinder's avatar
Morten Welinder committed
108
 * Returns: (skip) (transfer none): the initialized #GnmEvalPos (@ep).
109
 **/
110 111 112 113 114 115 116
GnmEvalPos *
eval_pos_init_pos (GnmEvalPos *ep, Sheet *sheet, GnmCellPos const *pos)
{
	g_return_val_if_fail (ep != NULL, NULL);
	g_return_val_if_fail (sheet != NULL, NULL);
	g_return_val_if_fail (pos != NULL, NULL);

Morten Welinder's avatar
Morten Welinder committed
117
	ep->eval = *pos;
118
	ep->sheet = sheet;
Morten Welinder's avatar
Morten Welinder committed
119 120
	ep->dep = NULL;
	ep->array_texpr = NULL;
121

122
	return ep;
123 124
}

125 126
/**
 * eval_pos_init_dep:
127
 * @ep: The position to init.
128 129
 * @dep:
 *
Morten Welinder's avatar
Morten Welinder committed
130
 * Returns: (skip) (transfer none): the initialized #GnmEvalPos (@ep).
131
 **/
Morten Welinder's avatar
Morten Welinder committed
132 133
GnmEvalPos *
eval_pos_init_dep (GnmEvalPos *ep, GnmDependent const *dep)
134
{
135
	g_return_val_if_fail (ep != NULL, NULL);
136 137
	g_return_val_if_fail (dep != NULL, NULL);

Morten Welinder's avatar
Morten Welinder committed
138
	ep->eval = *dependent_pos (dep);
139
	ep->sheet = dep->sheet;
Morten Welinder's avatar
Morten Welinder committed
140
	ep->dep = (GnmDependent *)dep;
Morten Welinder's avatar
Morten Welinder committed
141
	ep->array_texpr = NULL;
142

143
	return ep;
144 145
}

Morten Welinder's avatar
Morten Welinder committed
146
/**
147
 * eval_pos_init_editpos:
148
 * @ep: The position to init.
149
 * @sv: @Sheetview
Morten Welinder's avatar
Morten Welinder committed
150 151 152
 *
 * The function initializes an evalpos with the edit position from the
 * given sheetview.
Morten Welinder's avatar
Morten Welinder committed
153
 * Returns: (skip) (transfer none): the initialized #GnmEvalPos (@ep).
154
 **/
Morten Welinder's avatar
Morten Welinder committed
155 156 157 158
GnmEvalPos *
eval_pos_init_editpos (GnmEvalPos *ep, SheetView const *sv)
{
	g_return_val_if_fail (ep != NULL, NULL);
Morten Welinder's avatar
Morten Welinder committed
159
	g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
Morten Welinder's avatar
Morten Welinder committed
160 161 162 163 164 165

	return eval_pos_init (ep, sv_sheet (sv),
		sv->edit_pos.col, sv->edit_pos.row);
}

/**
166
 * eval_pos_init_cell:
167
 * @ep: The position to init.
168
 * @cell: A cell
Morten Welinder's avatar
Morten Welinder committed
169 170 171
 *
 * The function initializes an evalpos with the given cell
 *
Morten Welinder's avatar
Morten Welinder committed
172
 * Returns: (skip) (transfer none): the initialized #GnmEvalPos (@ep).
Morten Welinder's avatar
Morten Welinder committed
173
 */
Morten Welinder's avatar
Morten Welinder committed
174 175
GnmEvalPos *
eval_pos_init_cell (GnmEvalPos *ep, GnmCell const *cell)
176
{
177
	g_return_val_if_fail (ep != NULL, NULL);
178 179
	g_return_val_if_fail (cell != NULL, NULL);

Morten Welinder's avatar
Morten Welinder committed
180
	ep->eval = cell->pos;
181
	ep->sheet = cell->base.sheet;
Morten Welinder's avatar
Morten Welinder committed
182 183
	ep->dep = (GnmDependent *)GNM_CELL_TO_DEP (cell);
	ep->array_texpr = NULL;
184

185 186 187
	return ep;
}

188
/**
189
 * eval_pos_init_sheet:
190
 * @ep: The position to init.
191
 * @sheet: A sheet
192 193 194
 *
 * The function initializes an evalpos with the given sheet.
 *
Morten Welinder's avatar
Morten Welinder committed
195
 * Returns: (skip) (transfer none): the initialized #GnmEvalPos (@ep).
196
 */
Morten Welinder's avatar
Morten Welinder committed
197
GnmEvalPos *
Morten Welinder's avatar
Morten Welinder committed
198
eval_pos_init_sheet (GnmEvalPos *ep, Sheet const *sheet)
199 200 201 202
{
	g_return_val_if_fail (ep != NULL, NULL);
	g_return_val_if_fail (IS_SHEET (sheet), NULL);

203
	ep->eval.col = ep->eval.row = 0;
Morten Welinder's avatar
Morten Welinder committed
204
	ep->sheet = (Sheet *)sheet;
Morten Welinder's avatar
Morten Welinder committed
205 206
	ep->dep = NULL;
	ep->array_texpr = NULL;
207

208
	return ep;
209 210
}

Morten Welinder's avatar
Morten Welinder committed
211 212 213 214 215 216
gboolean
eval_pos_is_array_context (GnmEvalPos const *ep)
{
	return ep->array_texpr != NULL;
}

217

218
static GnmParsePos *
219
gnm_parse_pos_dup (GnmParsePos *pp)
220
{
221
	return g_memdup (pp, sizeof (*pp));
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
}

GType
gnm_parse_pos_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmParsePos",
			 (GBoxedCopyFunc)gnm_parse_pos_dup,
			 (GBoxedFreeFunc)g_free);
	}
	return t;
}

237
/**
238
 * parse_pos_init:
239
 * @pp: The position to init.
240 241 242 243
 * @sheet: The sheet being selected
 * @wb: The workbook being selected.
 * @row:
 * @col:
Jody Goldberg's avatar
Jody Goldberg committed
244 245 246
 *
 * Use either a sheet (preferred) or a workbook to initialize the supplied
 * ParsePosition.
Morten Welinder's avatar
Morten Welinder committed
247
 * Returns: (skip) (transfer none): the initialized #GnmParsePos (@pp).
248
 */
Morten Welinder's avatar
Morten Welinder committed
249
GnmParsePos *
Morten Welinder's avatar
Morten Welinder committed
250 251
parse_pos_init (GnmParsePos *pp, Workbook *wb, Sheet const *sheet,
		int col, int row)
252
{
Jody Goldberg's avatar
Jody Goldberg committed
253 254 255 256
	/* Global */
	if (wb == NULL && sheet == NULL)
		return NULL;

257 258
	g_return_val_if_fail (pp != NULL, NULL);

Morten Welinder's avatar
Morten Welinder committed
259
	pp->sheet = (Sheet *)sheet;
260 261 262 263 264 265 266
	pp->wb = sheet ? sheet->workbook : wb;
	pp->eval.col = col;
	pp->eval.row = row;

	return pp;
}

267
/**
268
 * parse_pos_init_dep:
269
 * @pp: The position to init.
270
 * @dep: The dependent
271
 *
Morten Welinder's avatar
Morten Welinder committed
272
 * Returns: (skip) (transfer none): the initialized #GnmParsePos (@pp).
273
 */
Morten Welinder's avatar
Morten Welinder committed
274 275
GnmParsePos *
parse_pos_init_dep (GnmParsePos *pp, GnmDependent const *dep)
276 277 278 279
{
	g_return_val_if_fail (pp != NULL, NULL);

	pp->sheet = dep->sheet;
280
	pp->wb = dep->sheet ? dep->sheet->workbook : NULL;
Morten Welinder's avatar
Morten Welinder committed
281
	pp->eval = *dependent_pos (dep);
282 283 284 285

	return pp;
}

286
/**
287
 * parse_pos_init_cell:
288
 * @pp: The position to init.
289 290
 * @cell: The cell
 *
Morten Welinder's avatar
Morten Welinder committed
291
 * Returns: (skip) (transfer none): the initialized #GnmParsePos (@pp).
292
 */
Morten Welinder's avatar
Morten Welinder committed
293 294
GnmParsePos *
parse_pos_init_cell (GnmParsePos *pp, GnmCell const *cell)
295 296
{
	g_return_val_if_fail (cell != NULL, NULL);
297 298
	g_return_val_if_fail (IS_SHEET (cell->base.sheet), NULL);
	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
 *
Morten Welinder's avatar
Morten Welinder committed
309
 * Returns: (skip) (transfer none): the initialized #GnmParsePos (@pp).
310
 */
Morten Welinder's avatar
Morten Welinder committed
311 312
GnmParsePos *
parse_pos_init_evalpos (GnmParsePos *pp, GnmEvalPos const *ep)
313 314 315 316 317 318
{
	g_return_val_if_fail (ep != NULL, NULL);

	return parse_pos_init (pp, NULL, ep->sheet, ep->eval.col, ep->eval.row);
}

319
/**
320
 * parse_pos_init_editpos:
321
 * @pp: The position to init.
322 323
 * @sv: sheet view
 *
Morten Welinder's avatar
Morten Welinder committed
324
 * Returns: (skip) (transfer none): the initialized #GnmParsePos (@pp).
325
 */
Morten Welinder's avatar
Morten Welinder committed
326 327
GnmParsePos *
parse_pos_init_editpos (GnmParsePos *pp, SheetView const *sv)
Jody Goldberg's avatar
Jody Goldberg committed
328
{
Morten Welinder's avatar
Morten Welinder committed
329
	g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
Jody Goldberg's avatar
Jody Goldberg committed
330 331 332 333 334

	return parse_pos_init (pp, NULL, sv_sheet (sv),
		sv->edit_pos.col, sv->edit_pos.row);
}

335
/**
336
 * parse_pos_init_sheet:
337
 * @pp: The position to init.
338 339
 * @sheet: The sheet
 *
Morten Welinder's avatar
Morten Welinder committed
340
 * Returns: (skip) (transfer none): the initialized #GnmParsePos (@pp).
341
 */
Morten Welinder's avatar
Morten Welinder committed
342
GnmParsePos *
Morten Welinder's avatar
Morten Welinder committed
343
parse_pos_init_sheet (GnmParsePos *pp, Sheet const *sheet)
344
{
345 346
	g_return_val_if_fail (pp != NULL, NULL);
	g_return_val_if_fail (IS_SHEET (sheet), NULL);
347 348 349
	return parse_pos_init (pp, NULL, sheet, 0, 0);
}

Jody Goldberg's avatar
Jody Goldberg committed
350 351
/********************************************************************************/

352 353

static GnmCellRef *
354
gnm_cellref_dup (GnmCellRef *cr)
355
{
356
	return g_memdup (cr, sizeof (*cr));
357 358 359 360 361 362 363 364 365 366 367 368 369 370
}

GType
gnm_cellref_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmCellRef",
			 (GBoxedCopyFunc)gnm_cellref_dup,
			 (GBoxedFreeFunc)g_free);
	}
	return t;
}
371
GnmCellRef *
372
gnm_cellref_init (GnmCellRef *ref, Sheet *sheet, int col, int row, gboolean relative)
373 374 375 376 377 378 379 380 381
{
	ref->sheet = sheet;
	ref->col   = col;
	ref->row   = row;
	ref->col_relative = ref->row_relative = relative;

	return ref;
}

382
gboolean
383
gnm_cellref_equal (GnmCellRef const *a, GnmCellRef const *b)
384
{
Morten Welinder's avatar
Morten Welinder committed
385 386 387 388 389
	return (a->col == b->col) &&
	       (a->col_relative == b->col_relative) &&
	       (a->row == b->row) &&
	       (a->row_relative == b->row_relative) &&
	       (a->sheet == b->sheet);
390 391
}

392
guint
393
gnm_cellref_hash (GnmCellRef const *cr)
394
{
395 396
	guint h = cr->row;
	h = (h << 16) | (h >> 16);
397
	h ^= ((guint)cr->col << 2);
398 399
	if (cr->col_relative) h ^= 1;
	if (cr->row_relative) h ^= 2;
400 401 402
	return h;
}

Jody Goldberg's avatar
Jody Goldberg committed
403
int
404
gnm_cellref_get_col (GnmCellRef const *ref, GnmEvalPos const *ep)
Jody Goldberg's avatar
Jody Goldberg committed
405 406
{
	g_return_val_if_fail (ref != NULL, 0);
407
	g_return_val_if_fail (ep != NULL, 0);
Jody Goldberg's avatar
Jody Goldberg committed
408

409
	if (ref->col_relative) {
410 411
		Sheet const *sheet = eval_sheet (ref->sheet, ep->sheet);
		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 415
		return res;
	}
Jody Goldberg's avatar
Jody Goldberg committed
416
	return ref->col;
417
}
Jody Goldberg's avatar
Jody Goldberg committed
418 419

int
420
gnm_cellref_get_row (GnmCellRef const *ref, GnmEvalPos const *ep)
Jody Goldberg's avatar
Jody Goldberg committed
421 422
{
	g_return_val_if_fail (ref != NULL, 0);
423
	g_return_val_if_fail (ep != NULL, 0);
Jody Goldberg's avatar
Jody Goldberg committed
424

425
	if (ref->row_relative) {
426 427
		Sheet const *sheet = eval_sheet (ref->sheet, ep->sheet);
		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 431
		return res;
	}
Jody Goldberg's avatar
Jody Goldberg committed
432 433 434
	return ref->row;
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448
static int
modulo (int i, int max)
{
	if (i < 0) {
		i %= max;
		if (i < 0)
			i += max;
	} else if (i >= max)
		i %= max;

	return i;
}


449
void
450
gnm_cellpos_init_cellref_ss (GnmCellPos *res, GnmCellRef const *cell_ref,
451
			     GnmCellPos const *pos, GnmSheetSize const *ss)
Jody Goldberg's avatar
Jody Goldberg committed
452 453
{
	g_return_if_fail (cell_ref != NULL);
454
	g_return_if_fail (res != NULL);
Jody Goldberg's avatar
Jody Goldberg committed
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;
Jody Goldberg's avatar
Jody Goldberg committed
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;
Jody Goldberg's avatar
Jody Goldberg committed
467 468
}

469 470 471 472
void
gnm_cellpos_init_cellref (GnmCellPos *res, GnmCellRef const *cell_ref,
			  GnmCellPos const *pos, Sheet const *base_sheet)
{
Morten Welinder's avatar
Morten Welinder committed
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
479
gnm_cellref_make_abs (GnmCellRef *dest, GnmCellRef const *src, GnmEvalPos const *ep)
480
{
481 482
	GnmCellPos pos;

483 484 485 486
	g_return_if_fail (dest != NULL);
	g_return_if_fail (src != NULL);
	g_return_if_fail (ep != NULL);

487
	gnm_cellpos_init_cellref (&pos, src, &ep->eval, ep->sheet);
488

489 490 491 492 493
	dest->sheet = src->sheet;
	dest->col = pos.col;
	dest->row = pos.row;
	dest->col_relative = FALSE;
	dest->row_relative = FALSE;
494 495
}

496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
void
gnm_cellref_set_col_ar (GnmCellRef *cr, GnmParsePos const *pp, gboolean abs_rel)
{
	if (cr->col_relative ^ abs_rel) {
		if (cr->col_relative)
			cr->col += pp->eval.col;
		else
			cr->col -= pp->eval.col;
		cr->col_relative = abs_rel;
	}
}

void
gnm_cellref_set_row_ar (GnmCellRef *cr, GnmParsePos const *pp, gboolean abs_rel)
{
	if (cr->row_relative ^ abs_rel) {
		if (cr->row_relative)
			cr->row += pp->eval.row;
		else
			cr->row -= pp->eval.row;
		cr->row_relative = abs_rel;
	}
}

520
gboolean
521
gnm_rangeref_equal (GnmRangeRef const *a, GnmRangeRef const *b)
522
{
523 524
	return  gnm_cellref_equal (&a->a, &b->a) &&
		gnm_cellref_equal (&a->b, &b->b);
525
}
526 527

guint
528
gnm_rangeref_hash (GnmRangeRef const *rr)
529
{
530 531 532 533
	guint h = gnm_cellref_hash (&rr->a);
	h = (h << 16) | (h >> 16);
	h ^= gnm_cellref_hash (&rr->b);
	return h;
534
}
535

Jody Goldberg's avatar
Jody Goldberg committed
536
GnmRangeRef *
537
gnm_rangeref_dup (GnmRangeRef const *rr)
538
{
539
	GnmRangeRef *res;
540

541
	g_return_val_if_fail (rr != NULL, NULL);
Morten Welinder's avatar
Morten Welinder committed
542

543 544 545
	res = g_new (GnmRangeRef, 1);
	*res = *rr;
	return res;
546 547
}

548 549 550 551 552 553 554 555 556 557 558 559 560
GType
gnm_rangeref_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmRangeRef",
			 (GBoxedCopyFunc)gnm_rangeref_dup,
			 (GBoxedFreeFunc)g_free);
	}
	return t;
}

561
void
562 563 564
gnm_rangeref_normalize_pp (GnmRangeRef const *ref, GnmParsePos const *pp,
			   Sheet **start_sheet, Sheet **end_sheet,
			   GnmRange *dest)
565
{
566 567
	GnmSheetSize const *ss;

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 576 577 578 579 580 581
	gnm_cellpos_init_cellref_ss (&dest->start, &ref->a, &pp->eval, ss);

	ss = *end_sheet
		? gnm_sheet_get_size (*end_sheet)
		: ss;
	gnm_cellpos_init_cellref_ss (&dest->end, &ref->b, &pp->eval, ss);

582
	range_normalize (dest);
583
}
584

585 586 587 588 589 590
/**
 * gnm_rangeref_normalize:
 *
 * Take a range_ref and normalize it by converting to absolute coords and
 * handling inversions.
 */
591 592 593 594 595 596 597 598 599 600
void
gnm_rangeref_normalize (GnmRangeRef const *ref, GnmEvalPos const *ep,
			Sheet **start_sheet, Sheet **end_sheet, GnmRange *dest)
{
	GnmParsePos pp;

	parse_pos_init_evalpos (&pp, ep);
	gnm_rangeref_normalize_pp (ref, &pp, start_sheet, end_sheet, dest);
}

601
guint
602
gnm_cellpos_hash (GnmCellPos const *key)
603
{
604 605 606 607
	guint h = key->row;
	h = (h << 16) | (h >> 16);
	h ^= key->col;
	return h;
608 609 610
}

gint
611
gnm_cellpos_equal (GnmCellPos const *a, GnmCellPos const *b)
612 613 614
{
	return (a->row == b->row && a->col == b->col);
}