GnuCash  5.6-150-g038405b370+
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 
40 static void editable_enters (GNCSearchCoreType *fe);
41 static void pass_parent (GNCSearchCoreType *fe, gpointer parent);
42 static void grab_focus (GNCSearchCoreType *fe);
43 static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe);
44 static gboolean gncs_validate (GNCSearchCoreType *fe);
45 static GtkWidget *gncs_get_widget(GNCSearchCoreType *fe);
46 static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe);
47 
48 static 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 
62 G_DEFINE_TYPE(GNCSearchString, gnc_search_string, GNC_TYPE_SEARCH_CORE_TYPE)
63 
64 static void
65 gnc_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 
84 static void
85 gnc_search_string_init (GNCSearchString *o)
86 {
87  o->value = NULL;
88  o->how = SEARCH_STRING_CONTAINS;
89  o->ign_case = TRUE;
90 }
91 
92 static void
93 gnc_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 
110 GNCSearchString *
111 gnc_search_string_new (void)
112 {
113  GNCSearchString *o = g_object_new(GNC_TYPE_SEARCH_STRING, NULL);
114  return o;
115 }
116 
117 void
118 gnc_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 
129 void
130 gnc_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 
137 void
138 gnc_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 
145 static gboolean
146 gncs_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 
212 static void
213 toggle_changed (GtkToggleButton *button, GNCSearchString *fe)
214 {
215  fe->ign_case = !gtk_toggle_button_get_active (button);
216 }
217 
218 static void
219 entry_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 
227 static GtkWidget *
228 make_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 
247 static void
248 grab_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 
259 static void
260 editable_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 
271 static void
272 pass_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 
282 static GtkWidget *
283 gncs_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 
315 static 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 
356 static 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...
Definition: qofquerycore.h:70
QofQueryCompare
Standard Query comparators, for how to compare objects in a predicate.
Definition: qofquerycore.h:54