1 /*
2  * search.c:  Search-and-replace for Gnumeric.
3  *
4  * Copyright (C) 2001-2009 Morten Welinder (terra@gnome.org)
5  */
6 
7 #include <gnumeric-config.h>
8 #include <gnm-i18n.h>
9 #include <gnumeric.h>
10 #include <search.h>
11 
12 #include <gutils.h>
13 #include <ranges.h>
14 #include <sheet.h>
15 #include <workbook.h>
16 #include <position.h>
17 #include <cell.h>
18 #include <number-match.h>
19 #include <value.h>
20 #include <sheet-object-cell-comment.h>
21 #include <gsf/gsf-impl-utils.h>
22 
23 #include <string.h>
24 #include <stdlib.h>
25 
26 static GObjectClass *parent_class;
27 
28 typedef struct {
29 	GOSearchReplaceClass base_class;
30 } GnmSearchReplaceClass;
31 
32 enum {
33 	PROP_0,
34 	PROP_IS_NUMBER,
35 	PROP_SEARCH_STRINGS,
36 	PROP_SEARCH_OTHER_VALUES,
37 	PROP_SEARCH_EXPRESSIONS,
38 	PROP_SEARCH_EXPRESSION_RESULTS,
39 	PROP_SEARCH_COMMENTS,
40 	PROP_SEARCH_SCRIPTS,
41 	PROP_INVERT,
42 	PROP_BY_ROW,
43 	PROP_QUERY,
44 	PROP_REPLACE_KEEP_STRINGS,
45 	PROP_SHEET,
46 	PROP_SCOPE,
47 	PROP_RANGE_TEXT
48 };
49 
50 /* ------------------------------------------------------------------------- */
51 
52 typedef struct {
53 	GnmCell *cell;
54 } GnmSearchReplaceValueResult;
55 static gboolean
56 gnm_search_replace_value (GnmSearchReplace *sr,
57 			  const GnmEvalPos *ep,
58 			  GnmSearchReplaceValueResult *res);
59 
60 /* ------------------------------------------------------------------------- */
61 
62 char *
gnm_search_normalize(const char * txt)63 gnm_search_normalize (const char *txt)
64 {
65 	return g_utf8_normalize (txt, -1, G_NORMALIZE_NFD);
66 }
67 
68 static char *
gnm_search_normalize_result(const char * txt)69 gnm_search_normalize_result (const char *txt)
70 {
71 	return g_utf8_normalize (txt, -1, G_NORMALIZE_NFC);
72 }
73 
74 /* ------------------------------------------------------------------------- */
75 
76 static gboolean
check_number(GnmSearchReplace * sr)77 check_number (GnmSearchReplace *sr)
78 {
79 	GODateConventions const *date_conv = sheet_date_conv (sr->sheet);
80 	GOSearchReplace *gosr = (GOSearchReplace *)sr;
81 	GnmValue *v = format_match_number (gosr->search_text, NULL, date_conv);
82 
83 	if (v) {
84 		gnm_float f = value_get_as_float (v);
85 		if (f < 0) {
86 			sr->low_number = gnm_add_epsilon (f);
87 			sr->high_number = gnm_sub_epsilon (f);
88 		} else {
89 			sr->low_number = gnm_sub_epsilon (f);
90 			sr->high_number = gnm_add_epsilon (f);
91 		}
92 		value_release (v);
93 		return TRUE;
94 	} else {
95 		sr->low_number = sr->high_number = gnm_nan;
96 		return FALSE;
97 	}
98 }
99 
100 static gboolean
gnm_search_match_value(GnmSearchReplace const * sr,GnmValue const * val)101 gnm_search_match_value (GnmSearchReplace const *sr, GnmValue const *val)
102 {
103 	gnm_float f;
104 	if (!VALUE_IS_NUMBER (val))
105 		return FALSE;
106 	f = value_get_as_float (val);
107 
108 	return (sr->low_number <= f && f <= sr->high_number);
109 }
110 
111 
112 char *
gnm_search_replace_verify(GnmSearchReplace * sr,gboolean repl)113 gnm_search_replace_verify (GnmSearchReplace *sr, gboolean repl)
114 {
115 	GError *error = NULL;
116 	GOSearchReplace *gosr = (GOSearchReplace *)sr;
117 	g_return_val_if_fail (sr != NULL, NULL);
118 
119 	if (!go_search_replace_verify (gosr, repl, &error)) {
120 		char *msg = g_strdup (error->message);
121 		g_error_free (error);
122 		return msg;
123 	}
124 
125 	if (sr->is_number && gosr->is_regexp)
126 		return g_strdup (_("Searching for regular expressions and numbers are mutually exclusive."));
127 
128 	if (sr->is_number) {
129 		if (!check_number (sr))
130 			return g_strdup (_("The search text must be a number."));
131 	}
132 
133 	if (sr->scope == GNM_SRS_RANGE) {
134 		GSList *range_list;
135 
136 		if (!sr->range_text || sr->range_text[0] == 0)
137 			return g_strdup (_("You must specify a range to search."));
138 
139 		if ((range_list = global_range_list_parse (sr->sheet, sr->range_text))
140 		    == NULL)
141 			return g_strdup (_("The search range is invalid."));
142 		range_list_destroy (range_list);
143 	}
144 
145 	return NULL;
146 }
147 
148 /* ------------------------------------------------------------------------- */
149 
150 static int
cb_order_sheet_row_col(void const * _a,void const * _b)151 cb_order_sheet_row_col (void const *_a, void const *_b)
152 {
153 	GnmEvalPos const *a = *(GnmEvalPos const **)_a;
154 	GnmEvalPos const *b = *(GnmEvalPos const **)_b;
155 	int i;
156 
157 	i = strcmp (a->sheet->name_unquoted_collate_key,
158 	            b->sheet->name_unquoted_collate_key);
159 
160 	/* By row number.  */
161 	if (!i) i = (a->eval.row - b->eval.row);
162 
163 	/* By column number.  */
164 	if (!i) i = (a->eval.col - b->eval.col);
165 
166 	return i;
167 }
168 
169 static int
cb_order_sheet_col_row(void const * _a,void const * _b)170 cb_order_sheet_col_row (void const *_a, void const *_b)
171 {
172 	GnmEvalPos const *a = *(GnmEvalPos const **)_a;
173 	GnmEvalPos const *b = *(GnmEvalPos const **)_b;
174 	int i;
175 
176 	i = strcmp (a->sheet->name_unquoted_collate_key,
177 	            b->sheet->name_unquoted_collate_key);
178 
179 	/* By column number.  */
180 	if (!i) i = (a->eval.col - b->eval.col);
181 
182 	/* By row number.  */
183 	if (!i) i = (a->eval.row - b->eval.row);
184 
185 	return i;
186 }
187 
188 static GnmValue *
search_collect_cells_cb(GnmCellIter const * iter,gpointer user)189 search_collect_cells_cb (GnmCellIter const *iter, gpointer user)
190 {
191 	GPtrArray *cells = user;
192 	GnmEvalPos *ep = g_new (GnmEvalPos, 1);
193 
194 	g_ptr_array_add (cells, eval_pos_init_cell (ep, iter->cell));
195 
196 	return NULL;
197 }
198 
199 /**
200  * gnm_search_collect_cells:
201  * @sr: #GnmSearchReplace
202  *
203  * Collect a list of all cells subject to search.
204  * Returns: (element-type GnmEvalPos) (transfer full): the newly created array.
205  **/
206 GPtrArray *
gnm_search_collect_cells(GnmSearchReplace * sr)207 gnm_search_collect_cells (GnmSearchReplace *sr)
208 {
209 	GPtrArray *cells;
210 
211 	switch (sr->scope) {
212 	case GNM_SRS_WORKBOOK:
213 		g_return_val_if_fail (sr->sheet != NULL, NULL);
214 		cells = workbook_cells (sr->sheet->workbook, TRUE,
215 					GNM_SHEET_VISIBILITY_HIDDEN);
216 		break;
217 
218 	case GNM_SRS_SHEET:
219 		cells = sheet_cell_positions (sr->sheet, TRUE);
220 		break;
221 
222 	case GNM_SRS_RANGE:
223 	{
224 		GSList *range_list;
225 		GnmEvalPos ep;
226 		cells = g_ptr_array_new ();
227 		range_list = global_range_list_parse (sr->sheet, sr->range_text);
228 		global_range_list_foreach (range_list,
229 			   eval_pos_init_sheet (&ep, sr->sheet),
230 			   CELL_ITER_IGNORE_BLANK,
231 			   search_collect_cells_cb, cells);
232 		range_list_destroy (range_list);
233 		break;
234 	}
235 
236 	default:
237 		cells = NULL;
238 		g_assert_not_reached ();
239 	}
240 
241 	/* Sort our cells.  */
242 	g_ptr_array_sort (cells,
243 			  sr->by_row ? cb_order_sheet_row_col : cb_order_sheet_col_row);
244 
245 	return cells;
246 }
247 
248 /**
249  * gnm_search_collect_cells_free:
250  * @cells: (element-type GnmEvalPos) (transfer full):
251  */
252 void
gnm_search_collect_cells_free(GPtrArray * cells)253 gnm_search_collect_cells_free (GPtrArray *cells)
254 {
255 	unsigned i;
256 
257 	for (i = 0; i < cells->len; i++)
258 		g_free (g_ptr_array_index (cells, i));
259 	g_ptr_array_free (cells, TRUE);
260 }
261 
262 /* ------------------------------------------------------------------------- */
263 /**
264  * gnm_search_filter_matching:
265  * @sr: The search spec.
266  * @cells: (element-type GnmEvalPos): Cell positions to filter, presumably a result of gnm_search_collect_cells.
267  *
268  * Returns: (element-type GnmSearchFilterResult) (transfer full): matches
269  */
270 
271 GPtrArray *
gnm_search_filter_matching(GnmSearchReplace * sr,const GPtrArray * cells)272 gnm_search_filter_matching (GnmSearchReplace *sr, const GPtrArray *cells)
273 {
274 	unsigned i;
275 	GPtrArray *result = g_ptr_array_new ();
276 
277 	if (sr->is_number)
278 		check_number (sr);
279 
280 	for (i = 0; i < cells->len; i++) {
281 		GnmSearchReplaceCellResult cell_res;
282 		GnmSearchReplaceValueResult value_res;
283 		GnmSearchReplaceCommentResult comment_res;
284 		gboolean found;
285 		const GnmEvalPos *ep = g_ptr_array_index (cells, i);
286 
287 		found = gnm_search_replace_cell (sr, ep, FALSE, &cell_res);
288 		g_free (cell_res.old_text);
289 		if (cell_res.cell != NULL && found != sr->invert) {
290 			GnmSearchFilterResult *item = g_new (GnmSearchFilterResult, 1);
291 			item->ep = *ep;
292 			item->locus = GNM_SRL_CONTENTS;
293 			g_ptr_array_add (result, item);
294 		}
295 
296 		found = gnm_search_replace_value (sr, ep, &value_res);
297 		if (value_res.cell != NULL && gnm_cell_has_expr (value_res.cell) && found != sr->invert) {
298 			GnmSearchFilterResult *item = g_new (GnmSearchFilterResult, 1);
299 			item->ep = *ep;
300 			item->locus = GNM_SRL_VALUE;
301 			g_ptr_array_add (result, item);
302 		}
303 
304 		found = gnm_search_replace_comment (sr, ep, FALSE, &comment_res);
305 		if (comment_res.comment != NULL && found != sr->invert) {
306 			GnmSearchFilterResult *item = g_new (GnmSearchFilterResult, 1);
307 			item->ep = *ep;
308 			item->locus = GNM_SRL_COMMENT;
309 			g_ptr_array_add (result, item);
310 		}
311 	}
312 
313 	return result;
314 }
315 
316 /**
317  * gnm_search_filter_matching_free:
318  * @matches: (element-type GnmSearchFilterResult) (transfer full): matches
319  */
320 void
gnm_search_filter_matching_free(GPtrArray * matches)321 gnm_search_filter_matching_free (GPtrArray *matches)
322 {
323 	unsigned i;
324 	for (i = 0; i < matches->len; i++)
325 		g_free (g_ptr_array_index (matches, i));
326 	g_ptr_array_free (matches, TRUE);
327 }
328 
329 /* ------------------------------------------------------------------------- */
330 
331 gboolean
gnm_search_replace_comment(GnmSearchReplace * sr,const GnmEvalPos * ep,gboolean repl,GnmSearchReplaceCommentResult * res)332 gnm_search_replace_comment (GnmSearchReplace *sr,
333 			    const GnmEvalPos *ep,
334 			    gboolean repl,
335 			    GnmSearchReplaceCommentResult *res)
336 {
337 	gboolean found;
338 	char *norm_text;
339 
340 	g_return_val_if_fail (res, FALSE);
341 
342 	res->comment = NULL;
343 	res->old_text = NULL;
344 	res->new_text = NULL;
345 
346 	g_return_val_if_fail (sr, FALSE);
347 
348 	if (!sr->search_comments) return FALSE;
349 	if (sr->is_number) return FALSE;
350 
351 	res->comment = sheet_get_comment (ep->sheet, &ep->eval);
352 	if (!res->comment) return FALSE;
353 
354 	res->old_text = cell_comment_text_get (res->comment);
355 
356 	norm_text = gnm_search_normalize (res->old_text);
357 
358 	if (repl) {
359 		res->new_text = go_search_replace_string (GO_SEARCH_REPLACE (sr),
360 							  norm_text);
361 		found = (res->new_text != NULL);
362 		if (found) {
363 			char *norm = gnm_search_normalize_result (res->new_text);
364 			g_free (res->new_text);
365 			res->new_text = norm;
366 		}
367 	} else
368 		found = go_search_match_string (GO_SEARCH_REPLACE (sr),
369 						norm_text);
370 
371 	g_free (norm_text);
372 
373 	return found;
374 }
375 
376 /* ------------------------------------------------------------------------- */
377 
378 gboolean
gnm_search_replace_cell(GnmSearchReplace * sr,const GnmEvalPos * ep,gboolean repl,GnmSearchReplaceCellResult * res)379 gnm_search_replace_cell (GnmSearchReplace *sr,
380 			 const GnmEvalPos *ep,
381 			 gboolean repl,
382 			 GnmSearchReplaceCellResult *res)
383 {
384 	GnmCell *cell;
385 	GnmValue *v;
386 	gboolean is_expr, is_value, is_string, is_other;
387 	gboolean found = FALSE;
388 
389 	g_return_val_if_fail (res, FALSE);
390 
391 	res->cell = NULL;
392 	res->old_text = NULL;
393 	res->new_text = NULL;
394 
395 	g_return_val_if_fail (sr, FALSE);
396 
397 	cell = res->cell = sheet_cell_get (ep->sheet, ep->eval.col, ep->eval.row);
398 	if (!cell) return FALSE;
399 
400 	v = cell->value;
401 
402 	is_expr = gnm_cell_has_expr (cell);
403 	is_value = !is_expr && !gnm_cell_is_empty (cell) && v;
404 	is_string = is_value && (VALUE_IS_STRING (v));
405 	is_other = is_value && !is_string;
406 
407 	if (sr->is_number) {
408 		if (!is_value || !VALUE_IS_NUMBER (v))
409 			return FALSE;
410 		return gnm_search_match_value (sr, v);
411 	}
412 
413 	if ((is_expr && sr->search_expressions) ||
414 	    (is_string && sr->search_strings) ||
415 	    (is_other && sr->search_other_values)) {
416 		char *actual_src;
417 		gboolean initial_quote;
418 
419 		res->old_text = gnm_cell_get_entered_text (cell);
420 		initial_quote = (is_string && res->old_text[0] == '\'');
421 
422 		actual_src = gnm_search_normalize (res->old_text + initial_quote);
423 
424 		if (repl) {
425 			res->new_text = go_search_replace_string (GO_SEARCH_REPLACE (sr),
426 								  actual_src);
427 			if (res->new_text) {
428 				char *norm = gnm_search_normalize_result (res->new_text);
429 				g_free (res->new_text);
430 				res->new_text = norm;
431 
432 				if (sr->replace_keep_strings && is_string) {
433 					/*
434 					 * The initial quote was not part of the s-a-r,
435 					 * so tack it back on.
436 					 */
437 					char *tmp = g_new (char, strlen (res->new_text) + 2);
438 					tmp[0] = '\'';
439 					strcpy (tmp + 1, res->new_text);
440 					g_free (res->new_text);
441 					res->new_text = tmp;
442 				}
443 				found = TRUE;
444 			}
445 		} else
446 			found = go_search_match_string (GO_SEARCH_REPLACE (sr), actual_src);
447 
448 		g_free (actual_src);
449 	}
450 
451 	return found;
452 }
453 
454 /* ------------------------------------------------------------------------- */
455 
456 static gboolean
gnm_search_replace_value(GnmSearchReplace * sr,const GnmEvalPos * ep,GnmSearchReplaceValueResult * res)457 gnm_search_replace_value (GnmSearchReplace *sr,
458 			  const GnmEvalPos *ep,
459 			  GnmSearchReplaceValueResult *res)
460 {
461 	GnmCell *cell;
462 
463 	g_return_val_if_fail (res, FALSE);
464 
465 	res->cell = NULL;
466 
467 	g_return_val_if_fail (sr, FALSE);
468 
469 	if (!sr->search_expression_results)
470 		return FALSE;
471 
472 	cell = res->cell = sheet_cell_get (ep->sheet, ep->eval.col, ep->eval.row);
473 	if (!cell || !gnm_cell_has_expr (cell) || !cell->value)
474 		return FALSE;
475 	else if (sr->is_number) {
476 		return gnm_search_match_value (sr, cell->value);
477 	} else {
478 		char *val = gnm_search_normalize (value_peek_string (cell->value));
479 		gboolean res = go_search_match_string (GO_SEARCH_REPLACE (sr), val);
480 		g_free (val);
481 		return res;
482 	}
483 }
484 
485 /* ------------------------------------------------------------------------- */
486 
487 void
gnm_search_replace_query_fail(GnmSearchReplace * sr,const GnmSearchReplaceCellResult * res)488 gnm_search_replace_query_fail (GnmSearchReplace *sr,
489 			       const GnmSearchReplaceCellResult *res)
490 {
491 	if (!sr->query_func)
492 		return;
493 
494 	sr->query_func (GNM_SRQ_FAIL, sr,
495 			res->cell, res->old_text, res->new_text);
496 }
497 
498 int
gnm_search_replace_query_cell(GnmSearchReplace * sr,const GnmSearchReplaceCellResult * res)499 gnm_search_replace_query_cell (GnmSearchReplace *sr,
500 			       const GnmSearchReplaceCellResult *res)
501 {
502 	if (!sr->query || !sr->query_func)
503 		return GTK_RESPONSE_YES;
504 
505 	return sr->query_func (GNM_SRQ_QUERY, sr,
506 			       res->cell, res->old_text, res->new_text);
507 }
508 
509 
510 int
gnm_search_replace_query_comment(GnmSearchReplace * sr,const GnmEvalPos * ep,const GnmSearchReplaceCommentResult * res)511 gnm_search_replace_query_comment (GnmSearchReplace *sr,
512 				  const GnmEvalPos *ep,
513 				  const GnmSearchReplaceCommentResult *res)
514 {
515 	if (!sr->query || !sr->query_func)
516 		return GTK_RESPONSE_YES;
517 
518 	return sr->query_func (GNM_SRQ_QUERY_COMMENT, sr,
519 			       ep->sheet, &ep->eval,
520 			       res->old_text, res->new_text);
521 }
522 
523 /* ------------------------------------------------------------------------- */
524 
525 GType
gnm_search_replace_scope_get_type(void)526 gnm_search_replace_scope_get_type (void)
527 {
528 	static GType etype = 0;
529 	if (etype == 0) {
530 		static const GEnumValue values[] = {
531 			{ GNM_SRS_WORKBOOK, "GNM_SRS_WORKBOOK", "workbook" },
532 			{ GNM_SRS_SHEET,    "GNM_SRS_SHEET",    "sheet" },
533 			{ GNM_SRS_RANGE,    "GNM_SRS_RANGE",    "range" },
534 			{ 0, NULL, NULL }
535 		};
536 		etype = g_enum_register_static ("GnmSearchReplaceScope", values);
537 	}
538 	return etype;
539 }
540 
541 /* ------------------------------------------------------------------------- */
542 
543 static void
gnm_search_replace_init(GObject * obj)544 gnm_search_replace_init (GObject *obj)
545 {
546 }
547 
548 /* ------------------------------------------------------------------------- */
549 
550 static void
gnm_search_replace_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)551 gnm_search_replace_get_property (GObject     *object,
552 				 guint        property_id,
553 				 GValue      *value,
554 				 GParamSpec  *pspec)
555 {
556 	GnmSearchReplace *sr = (GnmSearchReplace *)object;
557 
558 	switch (property_id) {
559 	case PROP_IS_NUMBER:
560 		g_value_set_boolean (value, sr->is_number);
561 		break;
562 	case PROP_SEARCH_STRINGS:
563 		g_value_set_boolean (value, sr->search_strings);
564 		break;
565 	case PROP_SEARCH_OTHER_VALUES:
566 		g_value_set_boolean (value, sr->search_other_values);
567 		break;
568 	case PROP_SEARCH_EXPRESSIONS:
569 		g_value_set_boolean (value, sr->search_expressions);
570 		break;
571 	case PROP_SEARCH_EXPRESSION_RESULTS:
572 		g_value_set_boolean (value, sr->search_expression_results);
573 		break;
574 	case PROP_SEARCH_COMMENTS:
575 		g_value_set_boolean (value, sr->search_comments);
576 		break;
577 	case PROP_SEARCH_SCRIPTS:
578 		g_value_set_boolean (value, sr->search_scripts);
579 		break;
580 	case PROP_INVERT:
581 		g_value_set_boolean (value, sr->invert);
582 		break;
583 	case PROP_BY_ROW:
584 		g_value_set_boolean (value, sr->by_row);
585 		break;
586 	case PROP_QUERY:
587 		g_value_set_boolean (value, sr->query);
588 		break;
589 	case PROP_REPLACE_KEEP_STRINGS:
590 		g_value_set_boolean (value, sr->replace_keep_strings);
591 		break;
592 	case PROP_SHEET:
593 		g_value_set_object (value, sr->sheet);
594 		break;
595 	case PROP_SCOPE:
596 		g_value_set_enum (value, sr->scope);
597 		break;
598 	case PROP_RANGE_TEXT:
599 		g_value_set_string (value, sr->range_text);
600 		break;
601 	default:
602 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
603 		break;
604 	}
605 }
606 
607 /* ------------------------------------------------------------------------- */
608 
609 static void
gnm_search_replace_set_sheet(GnmSearchReplace * sr,Sheet * sheet)610 gnm_search_replace_set_sheet (GnmSearchReplace *sr, Sheet *sheet)
611 {
612 	if (sheet)
613 		g_object_ref (sheet);
614 	if (sr->sheet)
615 		g_object_unref (sr->sheet);
616 	sr->sheet = sheet;
617 }
618 
619 static void
gnm_search_replace_set_range_text(GnmSearchReplace * sr,char const * text)620 gnm_search_replace_set_range_text (GnmSearchReplace *sr, char const *text)
621 {
622 	char *text_copy = g_strdup (text);
623 	g_free (sr->range_text);
624 	sr->range_text = text_copy;
625 }
626 
627 static void
gnm_search_replace_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)628 gnm_search_replace_set_property (GObject      *object,
629 				 guint         property_id,
630 				 GValue const *value,
631 				 GParamSpec   *pspec)
632 {
633 	GnmSearchReplace *sr = (GnmSearchReplace *)object;
634 
635 	switch (property_id) {
636 	case PROP_IS_NUMBER:
637 		sr->is_number = g_value_get_boolean (value);
638 		break;
639 	case PROP_SEARCH_STRINGS:
640 		sr->search_strings = g_value_get_boolean (value);
641 		break;
642 	case PROP_SEARCH_OTHER_VALUES:
643 		sr->search_other_values = g_value_get_boolean (value);
644 		break;
645 	case PROP_SEARCH_EXPRESSIONS:
646 		sr->search_expressions = g_value_get_boolean (value);
647 		break;
648 	case PROP_SEARCH_EXPRESSION_RESULTS:
649 		sr->search_expression_results = g_value_get_boolean (value);
650 		break;
651 	case PROP_SEARCH_COMMENTS:
652 		sr->search_comments = g_value_get_boolean (value);
653 		break;
654 	case PROP_SEARCH_SCRIPTS:
655 		sr->search_scripts = g_value_get_boolean (value);
656 		break;
657 	case PROP_INVERT:
658 		sr->invert = g_value_get_boolean (value);
659 		break;
660 	case PROP_BY_ROW:
661 		sr->by_row = g_value_get_boolean (value);
662 		break;
663 	case PROP_QUERY:
664 		sr->query = g_value_get_boolean (value);
665 		break;
666 	case PROP_REPLACE_KEEP_STRINGS:
667 		sr->replace_keep_strings = g_value_get_boolean (value);
668 		break;
669 	case PROP_SHEET:
670 		gnm_search_replace_set_sheet (sr, g_value_get_object (value));
671 		break;
672 	case PROP_SCOPE:
673 		sr->scope = g_value_get_enum (value);
674 		break;
675 	case PROP_RANGE_TEXT:
676 		gnm_search_replace_set_range_text (sr, g_value_get_string (value));
677 		break;
678 	default:
679 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
680 		break;
681 	}
682 }
683 
684 /* ------------------------------------------------------------------------- */
685 
686 static void
gnm_search_replace_finalize(GObject * obj)687 gnm_search_replace_finalize (GObject *obj)
688 {
689 	GnmSearchReplace *sr = (GnmSearchReplace *)obj;
690 
691 	gnm_search_replace_set_sheet (sr, NULL);
692 	g_free (sr->range_text);
693 
694 	G_OBJECT_CLASS (parent_class)->finalize (obj);
695 }
696 
697 /* ------------------------------------------------------------------------- */
698 
699 static void
gnm_search_replace_class_init(GObjectClass * gobject_class)700 gnm_search_replace_class_init (GObjectClass *gobject_class)
701 {
702 	parent_class = g_type_class_peek_parent (gobject_class);
703 
704 	gobject_class->finalize = gnm_search_replace_finalize;
705 	gobject_class->get_property = gnm_search_replace_get_property;
706 	gobject_class->set_property = gnm_search_replace_set_property;
707 
708 	g_object_class_install_property
709 		(gobject_class,
710 		 PROP_IS_NUMBER,
711 		 g_param_spec_boolean ("is-number",
712 				       P_("Is Number"),
713 				       P_("Search for Specific Number Regardless of Formatting?"),
714 				       FALSE,
715 				       GSF_PARAM_STATIC |
716 				       G_PARAM_READWRITE));
717 	g_object_class_install_property
718 		(gobject_class,
719 		 PROP_SEARCH_STRINGS,
720 		 g_param_spec_boolean ("search-strings",
721 				       P_("Search Strings"),
722 				       P_("Should strings be searched?"),
723 				       FALSE,
724 				       GSF_PARAM_STATIC |
725 				       G_PARAM_READWRITE));
726 	g_object_class_install_property
727 		(gobject_class,
728 		 PROP_SEARCH_OTHER_VALUES,
729 		 g_param_spec_boolean ("search-other-values",
730 				       P_("Search Other Values"),
731 				       P_("Should non-strings be searched?"),
732 				       FALSE,
733 				       GSF_PARAM_STATIC |
734 				       G_PARAM_READWRITE));
735 	g_object_class_install_property
736 		(gobject_class,
737 		 PROP_SEARCH_EXPRESSIONS,
738 		 g_param_spec_boolean ("search-expressions",
739 				       P_("Search Expressions"),
740 				       P_("Should expressions be searched?"),
741 				       FALSE,
742 				       GSF_PARAM_STATIC |
743 				       G_PARAM_READWRITE));
744 	g_object_class_install_property
745 		(gobject_class,
746 		 PROP_SEARCH_EXPRESSION_RESULTS,
747 		 g_param_spec_boolean ("search-expression-results",
748 				       P_("Search Expression Results"),
749 				       P_("Should the results of expressions be searched?"),
750 				       FALSE,
751 				       GSF_PARAM_STATIC |
752 				       G_PARAM_READWRITE));
753 	g_object_class_install_property
754 		(gobject_class,
755 		 PROP_SEARCH_COMMENTS,
756 		 g_param_spec_boolean ("search-comments",
757 				       P_("Search Comments"),
758 				       P_("Should cell comments be searched?"),
759 				       FALSE,
760 				       GSF_PARAM_STATIC |
761 				       G_PARAM_READWRITE));
762 	g_object_class_install_property
763 		(gobject_class,
764 		 PROP_SEARCH_SCRIPTS,
765 		 g_param_spec_boolean ("search-scripts",
766 				       P_("Search Scripts"),
767 				       P_("Should scrips (workbook, and worksheet) be searched?"),
768 				       FALSE,
769 				       GSF_PARAM_STATIC |
770 				       G_PARAM_READWRITE));
771 	g_object_class_install_property
772 		(gobject_class,
773 		 PROP_INVERT,
774 		 g_param_spec_boolean ("invert",
775 				       P_("Invert"),
776 				       P_("Collect non-matching items"),
777 				       FALSE,
778 				       GSF_PARAM_STATIC |
779 				       G_PARAM_READWRITE));
780 	g_object_class_install_property
781 		(gobject_class,
782 		 PROP_BY_ROW,
783 		 g_param_spec_boolean ("by-row",
784 				       P_("By Row"),
785 				       P_("Is the search order by row?"),
786 				       FALSE,
787 				       GSF_PARAM_STATIC |
788 				       G_PARAM_READWRITE));
789 	g_object_class_install_property
790 		(gobject_class,
791 		 PROP_QUERY,
792 		 g_param_spec_boolean ("query",
793 				       P_("Query"),
794 				       P_("Should we query for each replacement?"),
795 				       FALSE,
796 				       GSF_PARAM_STATIC |
797 				       G_PARAM_READWRITE));
798 	g_object_class_install_property
799 		(gobject_class,
800 		 PROP_REPLACE_KEEP_STRINGS,
801 		 g_param_spec_boolean ("replace-keep-strings",
802 				       P_("Keep Strings"),
803 				       P_("Should replacement keep strings as strings?"),
804 				       FALSE,
805 				       GSF_PARAM_STATIC |
806 				       G_PARAM_READWRITE));
807 	g_object_class_install_property
808 		(gobject_class,
809 		 PROP_SHEET,
810 		 g_param_spec_object ("sheet",
811 				      P_("Sheet"),
812 				      P_("The sheet in which to search."),
813 				      GNM_SHEET_TYPE,
814 				      GSF_PARAM_STATIC |
815 				      G_PARAM_READWRITE));
816 	g_object_class_install_property
817 		(gobject_class,
818 		 PROP_SCOPE,
819 		 g_param_spec_enum ("scope",
820 				    P_("Scope"),
821 				    P_("Where to search."),
822 				    GNM_SEARCH_REPLACE_SCOPE_TYPE,
823 				    GNM_SRS_SHEET,
824 				    GSF_PARAM_STATIC |
825 				    G_PARAM_READWRITE));
826 	g_object_class_install_property
827 		(gobject_class,
828 		 PROP_RANGE_TEXT,
829 		 g_param_spec_string ("range-text",
830 				      P_("Range as Text"),
831 				      P_("The range in which to search."),
832 				      NULL,
833 				      GSF_PARAM_STATIC |
834 				      G_PARAM_READWRITE));
835 }
836 
837 /* ------------------------------------------------------------------------- */
838 
839 GSF_CLASS (GnmSearchReplace, gnm_search_replace,
840 	   gnm_search_replace_class_init, gnm_search_replace_init, GO_TYPE_SEARCH_REPLACE)
841