1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
2 /* gtksourcesearchsettings.c
3  * This file is part of GtkSourceView
4  *
5  * Copyright (C) 2013 - Sébastien Wilmet <swilmet@gnome.org>
6  *
7  * GtkSourceView is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * GtkSourceView 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include "gtksourcesearchsettings.h"
27 #include "gtksourceview-i18n.h"
28 
29 /**
30  * SECTION:searchsettings
31  * @Short_description: Search settings
32  * @Title: GtkSourceSearchSettings
33  * @See_also: #GtkSourceSearchContext
34  *
35  * A #GtkSourceSearchSettings object represents the settings of a search. The
36  * search settings can be associated with one or several
37  * #GtkSourceSearchContext<!-- -->s.
38  */
39 
40 enum
41 {
42 	PROP_0,
43 	PROP_SEARCH_TEXT,
44 	PROP_CASE_SENSITIVE,
45 	PROP_AT_WORD_BOUNDARIES,
46 	PROP_WRAP_AROUND,
47 	PROP_REGEX_ENABLED
48 };
49 
50 struct _GtkSourceSearchSettingsPrivate
51 {
52 	gchar *search_text;
53 	guint case_sensitive : 1;
54 	guint at_word_boundaries : 1;
55 	guint wrap_around : 1;
56 	guint regex_enabled : 1;
57 };
58 
G_DEFINE_TYPE_WITH_PRIVATE(GtkSourceSearchSettings,gtk_source_search_settings,G_TYPE_OBJECT)59 G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceSearchSettings, gtk_source_search_settings, G_TYPE_OBJECT)
60 
61 static void
62 gtk_source_search_settings_finalize (GObject *object)
63 {
64 	GtkSourceSearchSettings *settings = GTK_SOURCE_SEARCH_SETTINGS (object);
65 
66 	g_free (settings->priv->search_text);
67 
68 	G_OBJECT_CLASS (gtk_source_search_settings_parent_class)->finalize (object);
69 }
70 
71 static void
gtk_source_search_settings_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)72 gtk_source_search_settings_get_property (GObject    *object,
73 					 guint       prop_id,
74 					 GValue     *value,
75 					 GParamSpec *pspec)
76 {
77 	GtkSourceSearchSettings *settings;
78 
79 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (object));
80 
81 	settings = GTK_SOURCE_SEARCH_SETTINGS (object);
82 
83 	switch (prop_id)
84 	{
85 		case PROP_SEARCH_TEXT:
86 			g_value_set_string (value, settings->priv->search_text);
87 			break;
88 
89 		case PROP_CASE_SENSITIVE:
90 			g_value_set_boolean (value, settings->priv->case_sensitive);
91 			break;
92 
93 		case PROP_AT_WORD_BOUNDARIES:
94 			g_value_set_boolean (value, settings->priv->at_word_boundaries);
95 			break;
96 
97 		case PROP_WRAP_AROUND:
98 			g_value_set_boolean (value, settings->priv->wrap_around);
99 			break;
100 
101 		case PROP_REGEX_ENABLED:
102 			g_value_set_boolean (value, settings->priv->regex_enabled);
103 			break;
104 
105 		default:
106 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
107 			break;
108 	}
109 }
110 
111 static void
gtk_source_search_settings_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)112 gtk_source_search_settings_set_property (GObject      *object,
113 					 guint         prop_id,
114 					 const GValue *value,
115 					 GParamSpec   *pspec)
116 {
117 	GtkSourceSearchSettings *settings;
118 
119 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (object));
120 
121 	settings = GTK_SOURCE_SEARCH_SETTINGS (object);
122 
123 	switch (prop_id)
124 	{
125 		case PROP_SEARCH_TEXT:
126 			gtk_source_search_settings_set_search_text (settings, g_value_get_string (value));
127 			break;
128 
129 		case PROP_CASE_SENSITIVE:
130 			settings->priv->case_sensitive = g_value_get_boolean (value);
131 			break;
132 
133 		case PROP_AT_WORD_BOUNDARIES:
134 			settings->priv->at_word_boundaries = g_value_get_boolean (value);
135 			break;
136 
137 		case PROP_WRAP_AROUND:
138 			settings->priv->wrap_around = g_value_get_boolean (value);
139 			break;
140 
141 		case PROP_REGEX_ENABLED:
142 			settings->priv->regex_enabled = g_value_get_boolean (value);
143 			break;
144 
145 		default:
146 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147 			break;
148 	}
149 }
150 
151 static void
gtk_source_search_settings_class_init(GtkSourceSearchSettingsClass * klass)152 gtk_source_search_settings_class_init (GtkSourceSearchSettingsClass *klass)
153 {
154 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
155 
156 	object_class->finalize = gtk_source_search_settings_finalize;
157 	object_class->get_property = gtk_source_search_settings_get_property;
158 	object_class->set_property = gtk_source_search_settings_set_property;
159 
160 	/**
161 	 * GtkSourceSearchSettings:search-text:
162 	 *
163 	 * A search string, or %NULL if the search is disabled. If the regular
164 	 * expression search is enabled, #GtkSourceSearchSettings:search-text is
165 	 * the pattern.
166 	 *
167 	 * Since: 3.10
168 	 */
169 	g_object_class_install_property (object_class,
170 					 PROP_SEARCH_TEXT,
171 					 g_param_spec_string ("search-text",
172 							      "Search text",
173 							      "The text to search",
174 							      NULL,
175 							      G_PARAM_READWRITE |
176 							      G_PARAM_CONSTRUCT |
177 							      G_PARAM_STATIC_STRINGS));
178 
179 	/**
180 	 * GtkSourceSearchSettings:case-sensitive:
181 	 *
182 	 * Whether the search is case sensitive.
183 	 *
184 	 * Since: 3.10
185 	 */
186 	g_object_class_install_property (object_class,
187 					 PROP_CASE_SENSITIVE,
188 					 g_param_spec_boolean ("case-sensitive",
189 							       "Case sensitive",
190 							       "Case sensitive",
191 							       FALSE,
192 							       G_PARAM_READWRITE |
193 							       G_PARAM_CONSTRUCT |
194 							       G_PARAM_STATIC_STRINGS));
195 
196 	/**
197 	 * GtkSourceSearchSettings:at-word-boundaries:
198 	 *
199 	 * If %TRUE, a search match must start and end a word. The match can
200 	 * span multiple words.
201 	 *
202 	 * Since: 3.10
203 	 */
204 	g_object_class_install_property (object_class,
205 					 PROP_AT_WORD_BOUNDARIES,
206 					 g_param_spec_boolean ("at-word-boundaries",
207 							       "At word boundaries",
208 							       "Search at word boundaries",
209 							       FALSE,
210 							       G_PARAM_READWRITE |
211 							       G_PARAM_CONSTRUCT |
212 							       G_PARAM_STATIC_STRINGS));
213 
214 	/**
215 	 * GtkSourceSearchSettings:wrap-around:
216 	 *
217 	 * For a forward search, continue at the beginning of the buffer if no
218 	 * search occurrence is found. For a backward search, continue at the
219 	 * end of the buffer.
220 	 *
221 	 * Since: 3.10
222 	 */
223 	g_object_class_install_property (object_class,
224 					 PROP_WRAP_AROUND,
225 					 g_param_spec_boolean ("wrap-around",
226 							       "Wrap around",
227 							       "Wrap around",
228 							       FALSE,
229 							       G_PARAM_READWRITE |
230 							       G_PARAM_CONSTRUCT |
231 							       G_PARAM_STATIC_STRINGS));
232 
233 	/**
234 	 * GtkSourceSearchSettings:regex-enabled:
235 	 *
236 	 * Search by regular expressions with
237 	 * #GtkSourceSearchSettings:search-text as the pattern.
238 	 *
239 	 * Since: 3.10
240 	 */
241 	g_object_class_install_property (object_class,
242 					 PROP_REGEX_ENABLED,
243 					 g_param_spec_boolean ("regex-enabled",
244 							       "Regex enabled",
245 							       "Whether to search by regular expression",
246 							       FALSE,
247 							       G_PARAM_READWRITE |
248 							       G_PARAM_CONSTRUCT |
249 							       G_PARAM_STATIC_STRINGS));
250 }
251 
252 static void
gtk_source_search_settings_init(GtkSourceSearchSettings * self)253 gtk_source_search_settings_init (GtkSourceSearchSettings *self)
254 {
255 	self->priv = gtk_source_search_settings_get_instance_private (self);
256 }
257 
258 /**
259  * gtk_source_search_settings_new:
260  *
261  * Creates a new search settings object.
262  *
263  * Returns: a new search settings object.
264  * Since: 3.10
265  */
266 GtkSourceSearchSettings *
gtk_source_search_settings_new(void)267 gtk_source_search_settings_new (void)
268 {
269 	return g_object_new (GTK_SOURCE_TYPE_SEARCH_SETTINGS, NULL);
270 }
271 
272 /**
273  * gtk_source_search_settings_set_search_text:
274  * @settings: a #GtkSourceSearchSettings.
275  * @search_text: (nullable): the nul-terminated text to search, or %NULL to disable the search.
276  *
277  * Sets the text to search. If @search_text is %NULL or is empty, the search
278  * will be disabled. A copy of @search_text will be made, so you can safely free
279  * @search_text after a call to this function.
280  *
281  * You may be interested to call gtk_source_utils_unescape_search_text() before
282  * this function.
283  *
284  * Since: 3.10
285  */
286 void
gtk_source_search_settings_set_search_text(GtkSourceSearchSettings * settings,const gchar * search_text)287 gtk_source_search_settings_set_search_text (GtkSourceSearchSettings *settings,
288 					    const gchar             *search_text)
289 {
290 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
291 	g_return_if_fail (search_text == NULL || g_utf8_validate (search_text, -1, NULL));
292 
293 	if ((settings->priv->search_text == NULL &&
294 	     (search_text == NULL || search_text[0] == '\0')) ||
295 	    g_strcmp0 (settings->priv->search_text, search_text) == 0)
296 	{
297 		return;
298 	}
299 
300 	g_free (settings->priv->search_text);
301 
302 	if (search_text == NULL || search_text[0] == '\0')
303 	{
304 		settings->priv->search_text = NULL;
305 	}
306 	else
307 	{
308 		settings->priv->search_text = g_strdup (search_text);
309 	}
310 
311 	g_object_notify (G_OBJECT (settings), "search-text");
312 }
313 
314 /**
315  * gtk_source_search_settings_get_search_text:
316  * @settings: a #GtkSourceSearchSettings.
317  *
318  * Gets the text to search. The return value must not be freed.
319  *
320  * You may be interested to call gtk_source_utils_escape_search_text() after
321  * this function.
322  *
323  * Returns: (nullable): the text to search, or %NULL if the search is disabled.
324  * Since: 3.10
325  */
326 const gchar *
gtk_source_search_settings_get_search_text(GtkSourceSearchSettings * settings)327 gtk_source_search_settings_get_search_text (GtkSourceSearchSettings *settings)
328 {
329 	g_return_val_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings), NULL);
330 
331 	return settings->priv->search_text;
332 }
333 
334 /**
335  * gtk_source_search_settings_set_case_sensitive:
336  * @settings: a #GtkSourceSearchSettings.
337  * @case_sensitive: the setting.
338  *
339  * Enables or disables the case sensitivity for the search.
340  *
341  * Since: 3.10
342  */
343 void
gtk_source_search_settings_set_case_sensitive(GtkSourceSearchSettings * settings,gboolean case_sensitive)344 gtk_source_search_settings_set_case_sensitive (GtkSourceSearchSettings *settings,
345 					       gboolean                 case_sensitive)
346 {
347 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
348 
349 	case_sensitive = case_sensitive != FALSE;
350 
351 	if (settings->priv->case_sensitive != case_sensitive)
352 	{
353 		settings->priv->case_sensitive = case_sensitive;
354 		g_object_notify (G_OBJECT (settings), "case-sensitive");
355 	}
356 }
357 
358 /**
359  * gtk_source_search_settings_get_case_sensitive:
360  * @settings: a #GtkSourceSearchSettings.
361  *
362  * Returns: whether the search is case sensitive.
363  * Since: 3.10
364  */
365 gboolean
gtk_source_search_settings_get_case_sensitive(GtkSourceSearchSettings * settings)366 gtk_source_search_settings_get_case_sensitive (GtkSourceSearchSettings *settings)
367 {
368 	g_return_val_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings), FALSE);
369 
370 	return settings->priv->case_sensitive;
371 }
372 
373 /**
374  * gtk_source_search_settings_set_at_word_boundaries:
375  * @settings: a #GtkSourceSearchSettings.
376  * @at_word_boundaries: the setting.
377  *
378  * Change whether the search is done at word boundaries. If @at_word_boundaries
379  * is %TRUE, a search match must start and end a word. The match can span
380  * multiple words. See also gtk_text_iter_starts_word() and
381  * gtk_text_iter_ends_word().
382  *
383  * Since: 3.10
384  */
385 void
gtk_source_search_settings_set_at_word_boundaries(GtkSourceSearchSettings * settings,gboolean at_word_boundaries)386 gtk_source_search_settings_set_at_word_boundaries (GtkSourceSearchSettings *settings,
387 						   gboolean                 at_word_boundaries)
388 {
389 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
390 
391 	at_word_boundaries = at_word_boundaries != FALSE;
392 
393 	if (settings->priv->at_word_boundaries != at_word_boundaries)
394 	{
395 		settings->priv->at_word_boundaries = at_word_boundaries;
396 		g_object_notify (G_OBJECT (settings), "at-word-boundaries");
397 	}
398 }
399 
400 /**
401  * gtk_source_search_settings_get_at_word_boundaries:
402  * @settings: a #GtkSourceSearchSettings.
403  *
404  * Returns: whether to search at word boundaries.
405  * Since: 3.10
406  */
407 gboolean
gtk_source_search_settings_get_at_word_boundaries(GtkSourceSearchSettings * settings)408 gtk_source_search_settings_get_at_word_boundaries (GtkSourceSearchSettings *settings)
409 {
410 	g_return_val_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings), FALSE);
411 
412 	return settings->priv->at_word_boundaries;
413 }
414 
415 /**
416  * gtk_source_search_settings_set_wrap_around:
417  * @settings: a #GtkSourceSearchSettings.
418  * @wrap_around: the setting.
419  *
420  * Enables or disables the wrap around search. If @wrap_around is %TRUE, the
421  * forward search continues at the beginning of the buffer if no search
422  * occurrences are found. Similarly, the backward search continues to search at
423  * the end of the buffer.
424  *
425  * Since: 3.10
426  */
427 void
gtk_source_search_settings_set_wrap_around(GtkSourceSearchSettings * settings,gboolean wrap_around)428 gtk_source_search_settings_set_wrap_around (GtkSourceSearchSettings *settings,
429 					    gboolean                 wrap_around)
430 {
431 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
432 
433 	wrap_around = wrap_around != FALSE;
434 
435 	if (settings->priv->wrap_around != wrap_around)
436 	{
437 		settings->priv->wrap_around = wrap_around;
438 		g_object_notify (G_OBJECT (settings), "wrap-around");
439 	}
440 }
441 
442 /**
443  * gtk_source_search_settings_get_wrap_around:
444  * @settings: a #GtkSourceSearchSettings.
445  *
446  * Returns: whether to wrap around the search.
447  * Since: 3.10
448  */
449 gboolean
gtk_source_search_settings_get_wrap_around(GtkSourceSearchSettings * settings)450 gtk_source_search_settings_get_wrap_around (GtkSourceSearchSettings *settings)
451 {
452 	g_return_val_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings), FALSE);
453 
454 	return settings->priv->wrap_around;
455 }
456 
457 /**
458  * gtk_source_search_settings_set_regex_enabled:
459  * @settings: a #GtkSourceSearchSettings.
460  * @regex_enabled: the setting.
461  *
462  * Enables or disables whether to search by regular expressions.
463  * If enabled, the #GtkSourceSearchSettings:search-text property contains the
464  * pattern of the regular expression.
465  *
466  * #GtkSourceSearchContext uses #GRegex when regex search is enabled. See the
467  * [Regular expression syntax](https://developer.gnome.org/glib/stable/glib-regex-syntax.html)
468  * page in the GLib reference manual.
469  *
470  * Since: 3.10
471  */
472 void
gtk_source_search_settings_set_regex_enabled(GtkSourceSearchSettings * settings,gboolean regex_enabled)473 gtk_source_search_settings_set_regex_enabled (GtkSourceSearchSettings *settings,
474 					      gboolean                 regex_enabled)
475 {
476 	g_return_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
477 
478 	regex_enabled = regex_enabled != FALSE;
479 
480 	if (settings->priv->regex_enabled != regex_enabled)
481 	{
482 		settings->priv->regex_enabled = regex_enabled;
483 		g_object_notify (G_OBJECT (settings), "regex-enabled");
484 	}
485 }
486 
487 /**
488  * gtk_source_search_settings_get_regex_enabled:
489  * @settings: a #GtkSourceSearchSettings.
490  *
491  * Returns: whether to search by regular expressions.
492  * Since: 3.10
493  */
494 gboolean
gtk_source_search_settings_get_regex_enabled(GtkSourceSearchSettings * settings)495 gtk_source_search_settings_get_regex_enabled (GtkSourceSearchSettings *settings)
496 {
497 	g_return_val_if_fail (GTK_SOURCE_IS_SEARCH_SETTINGS (settings), FALSE);
498 
499 	return settings->priv->regex_enabled;
500 }
501