GnuCash c935c2f+
Loading...
Searching...
No Matches
search-string.c
1/*
2 * Copyright (C) 2002 Derek Atkins
3 *
4 * Authors: Derek Atkins <warlord@MIT.EDU>
5 *
6 * Copyright (c) 2006 David Hampton <hampton@employees.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
11 * the 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 GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30#include <string.h>
31#include <sys/types.h>
32#include <regex.h>
33
34#include "search-string.h"
35#include "search-core-utils.h"
36#include "qof.h"
37
38#define d(x)
39
40static void editable_enters (GNCSearchCoreType *fe);
41static void pass_parent (GNCSearchCoreType *fe, gpointer parent);
42static void grab_focus (GNCSearchCoreType *fe);
43static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe);
44static gboolean gncs_validate (GNCSearchCoreType *fe);
45static GtkWidget *gncs_get_widget(GNCSearchCoreType *fe);
46static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe);
47
48static void gnc_search_string_finalize (GObject *obj);
49
51{
52 GNCSearchCoreType parent_instance;
53
54 GNCSearchString_Type how;
55 gboolean ign_case;
56 char * value;
57
58 GtkWidget *entry;
59 GtkWindow *parent;
60};
61
62G_DEFINE_TYPE(GNCSearchString, gnc_search_string, GNC_TYPE_SEARCH_CORE_TYPE)
63
64static void
65gnc_search_string_class_init (GNCSearchStringClass *klass)
66{
67 GObjectClass *object_class;
68 GNCSearchCoreTypeClass *gnc_search_core_type = (GNCSearchCoreTypeClass *)klass;
69
70 object_class = G_OBJECT_CLASS (klass);
71
72 object_class->finalize = gnc_search_string_finalize;
73
74 /* override methods */
75 gnc_search_core_type->editable_enters = editable_enters;
76 gnc_search_core_type->pass_parent = pass_parent;
77 gnc_search_core_type->grab_focus = grab_focus;
78 gnc_search_core_type->validate = gncs_validate;
79 gnc_search_core_type->get_widget = gncs_get_widget;
80 gnc_search_core_type->get_predicate = gncs_get_predicate;
81 gnc_search_core_type->clone = gncs_clone;
82}
83
84static void
85gnc_search_string_init (GNCSearchString *o)
86{
87 o->value = NULL;
88 o->how = SEARCH_STRING_CONTAINS;
89 o->ign_case = TRUE;
90}
91
92static void
93gnc_search_string_finalize (GObject *obj)
94{
95 GNCSearchString *o = (GNCSearchString *)obj;
96 g_assert (GNC_IS_SEARCH_STRING (o));
97
98 g_free (o->value);
99
100 G_OBJECT_CLASS (gnc_search_string_parent_class)->finalize(obj);
101}
102
110GNCSearchString *
111gnc_search_string_new (void)
112{
113 GNCSearchString *o = g_object_new(GNC_TYPE_SEARCH_STRING, NULL);
114 return o;
115}
116
117void
118gnc_search_string_set_value (GNCSearchString *fi, const char *value)
119{
120 g_return_if_fail (fi);
121 g_return_if_fail (GNC_IS_SEARCH_STRING (fi));
122
123 if (fi->value)
124 g_free (fi->value);
125
126 fi->value = g_strdup (value);
127}
128
129void
130gnc_search_string_set_how (GNCSearchString *fi, GNCSearchString_Type how)
131{
132 g_return_if_fail (fi);
133 g_return_if_fail (GNC_IS_SEARCH_STRING (fi));
134 fi->how = how;
135}
136
137void
138gnc_search_string_set_case (GNCSearchString *fi, gboolean ignore_case)
139{
140 g_return_if_fail (fi);
141 g_return_if_fail (GNC_IS_SEARCH_STRING (fi));
142 fi->ign_case = ignore_case;
143}
144
145static gboolean
146gncs_validate (GNCSearchCoreType *fe)
147{
148 GNCSearchString *fi = (GNCSearchString *)fe;
149 gboolean valid = TRUE;
150
151 g_return_val_if_fail (fi, FALSE);
152 g_return_val_if_fail (GNC_IS_SEARCH_STRING (fi), FALSE);
153
154 if (!fi->value || *(fi->value) == '\0')
155 {
156 GtkWidget *dialog;
157 dialog = gtk_message_dialog_new (GTK_WINDOW(fi->parent),
158 GTK_DIALOG_MODAL,
159 GTK_MESSAGE_ERROR,
160 GTK_BUTTONS_OK,
161 "%s",
162 _("You need to enter some search text."));
163 gtk_dialog_run (GTK_DIALOG (dialog));
164 gtk_widget_destroy(dialog);
165 return FALSE;
166 }
167
168 if (fi->how == SEARCH_STRING_MATCHES_REGEX ||
169 fi->how == SEARCH_STRING_NOT_MATCHES_REGEX)
170 {
171 regex_t regexpat; /* regex pattern */
172 gint regerr;
173 int flags = REG_EXTENDED;
174
175 if (fi->ign_case)
176 flags |= REG_ICASE;
177
178 regerr = regcomp (&regexpat, fi->value, flags);
179 if (regerr)
180 {
181 GtkWidget *dialog;
182 gchar *regmsg, *errmsg;
183 size_t reglen;
184
185 /* regerror gets called twice to get the full error string
186 length to do proper posix error reporting */
187 reglen = regerror (regerr, &regexpat, 0, 0);
188 regmsg = g_malloc0 (reglen + 1);
189 regerror (regerr, &regexpat, regmsg, reglen);
190
191 errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
192 fi->value, regmsg);
193 g_free (regmsg);
194
195 dialog = gtk_message_dialog_new (GTK_WINDOW(fi->parent),
196 GTK_DIALOG_MODAL,
197 GTK_MESSAGE_ERROR,
198 GTK_BUTTONS_OK,
199 "%s", errmsg);
200 gtk_dialog_run (GTK_DIALOG (dialog));
201 gtk_widget_destroy(dialog);
202 g_free (errmsg);
203 valid = FALSE;
204 }
205
206 regfree (&regexpat);
207 }
208
209 return valid;
210}
211
212static void
213toggle_changed (GtkToggleButton *button, GNCSearchString *fe)
214{
215 fe->ign_case = !gtk_toggle_button_get_active (button);
216}
217
218static void
219entry_changed (GtkEntry *entry, GNCSearchString *fe)
220{
221 const char *new_str;
222
223 new_str = gtk_entry_get_text(entry);
224 gnc_search_string_set_value (fe, new_str);
225}
226
227static GtkWidget *
228make_menu (GNCSearchCoreType *fe)
229{
230 GNCSearchString *fi = (GNCSearchString *)fe;
231 GtkComboBox *combo;
232
233 combo = GTK_COMBO_BOX(gnc_combo_box_new_search());
234
235 gnc_combo_box_search_add(combo, _("contains"), SEARCH_STRING_CONTAINS);
236 gnc_combo_box_search_add(combo, _("equals"), SEARCH_STRING_EQUAL);
237 gnc_combo_box_search_add(combo, _("matches regex"),
238 SEARCH_STRING_MATCHES_REGEX);
239 gnc_combo_box_search_add(combo, _("does not match regex"),
240 SEARCH_STRING_NOT_MATCHES_REGEX);
241 gnc_combo_box_search_changed(combo, &fi->how);
242 gnc_combo_box_search_set_active(combo, fi->how ? fi->how : SEARCH_STRING_CONTAINS);
243
244 return GTK_WIDGET(combo);
245}
246
247static void
248grab_focus (GNCSearchCoreType *fe)
249{
250 GNCSearchString *fi = (GNCSearchString *)fe;
251
252 g_return_if_fail (fi);
253 g_return_if_fail (GNC_IS_SEARCH_STRING (fi));
254
255 if (fi->entry)
256 gtk_widget_grab_focus (fi->entry);
257}
258
259static void
260editable_enters (GNCSearchCoreType *fe)
261{
262 GNCSearchString *fi = (GNCSearchString *)fe;
263
264 g_return_if_fail (fi);
265 g_return_if_fail (GNC_IS_SEARCH_STRING (fi));
266
267 if (fi->entry)
268 gtk_entry_set_activates_default(GTK_ENTRY (fi->entry), TRUE);
269}
270
271static void
272pass_parent (GNCSearchCoreType *fe, gpointer parent)
273{
274 GNCSearchString *fi = (GNCSearchString *)fe;
275
276 g_return_if_fail (fi);
277 g_return_if_fail (GNC_IS_SEARCH_STRING (fi));
278
279 fi->parent = GTK_WINDOW(parent);
280}
281
282static GtkWidget *
283gncs_get_widget (GNCSearchCoreType *fe)
284{
285 GtkWidget *entry, *toggle, *menu, *box;
286 GNCSearchString *fi = (GNCSearchString *)fe;
287
288 g_return_val_if_fail (fi, NULL);
289 g_return_val_if_fail (GNC_IS_SEARCH_STRING (fi), NULL);
290
291 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
292 gtk_box_set_homogeneous (GTK_BOX (box), FALSE);
293
294 /* Build and connect the option menu */
295 menu = make_menu (fe);
296 gtk_box_pack_start (GTK_BOX (box), menu, FALSE, FALSE, 3);
297
298 /* Build and connect the entry window */
299 entry = gtk_entry_new ();
300 if (fi->value)
301 gtk_entry_set_text (GTK_ENTRY (entry), fi->value);
302 g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (entry_changed), fe);
303 gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 3);
304 fi->entry = entry;
305
306 /* Build and connect the case-sensitive check button; defaults to off */
307 toggle = gtk_check_button_new_with_label (_("Match case"));
308 g_signal_connect (G_OBJECT(toggle), "toggled", G_CALLBACK (toggle_changed), fe);
309 gtk_box_pack_start (GTK_BOX (box), toggle, FALSE, FALSE, 3);
310
311 /* And return the box */
312 return box;
313}
314
315static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe)
316{
317 GNCSearchString *ss = (GNCSearchString *)fe;
318 QofQueryCompare how;
319 QofStringMatch options = QOF_STRING_MATCH_NORMAL;
320 gboolean is_regex = FALSE;
321
322 g_return_val_if_fail (ss, NULL);
323 g_return_val_if_fail (GNC_IS_SEARCH_STRING (ss), NULL);
324
325 switch (ss->how)
326 {
327 case SEARCH_STRING_MATCHES_REGEX:
328 is_regex = TRUE;
329 /* FALL THROUGH */
330 case SEARCH_STRING_CONTAINS:
331 how = QOF_COMPARE_CONTAINS;
332 break;
333 case SEARCH_STRING_EQUAL:
334 how = QOF_COMPARE_EQUAL;
335 break;
336 case SEARCH_STRING_NOT_MATCHES_REGEX:
337 is_regex = TRUE;
338 /* FALL THROUGH */
339 case SEARCH_STRING_NOT_CONTAINS:
340 how = QOF_COMPARE_NCONTAINS;
341 break;
342 case SEARCH_STRING_NOT_EQUAL:
343 how = QOF_COMPARE_NEQ;
344 break;
345 default:
346 g_warning ("invalid string choice: %d", ss->how);
347 return NULL;
348 }
349
350 if (ss->ign_case)
351 options = QOF_STRING_MATCH_CASEINSENSITIVE;
352
353 return qof_query_string_predicate (how, ss->value, options, is_regex);
354}
355
356static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe)
357{
358 GNCSearchString *se, *fse = (GNCSearchString *)fe;
359
360 g_return_val_if_fail (fse, NULL);
361 g_return_val_if_fail (GNC_IS_SEARCH_STRING (fse), NULL);
362
363 se = gnc_search_string_new ();
364 gnc_search_string_set_value (se, fse->value);
365 gnc_search_string_set_how (se, fse->how);
366 gnc_search_string_set_case (se, fse->ign_case);
367
368 return (GNCSearchCoreType *)se;
369}
QofStringMatch
List of known core query data-types... Each core query type defines it's set of optional "comparator ...
QofQueryCompare
Standard Query comparators, for how to compare objects in a predicate.