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