GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
search-string.c
1 /*
2  * Copyright (C) 2002 Derek Atkins
3  *
4  * Authors: Derek Atkins <[email protected]>
5  *
6  * Copyright (c) 2006 David Hampton <[email protected]>
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 grab_focus (GNCSearchCoreType *fe);
42 static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe);
43 static gboolean gncs_validate (GNCSearchCoreType *fe);
44 static GtkWidget *gncs_get_widget(GNCSearchCoreType *fe);
45 static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe);
46 
47 static void gnc_search_string_class_init (GNCSearchStringClass *klass);
48 static void gnc_search_string_init (GNCSearchString *gspaper);
49 static void gnc_search_string_finalize (GObject *obj);
50 
52 
54 {
55  GtkWidget *entry;
56 };
57 
58 #define _PRIVATE(o) \
59  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_SEARCH_STRING, GNCSearchStringPrivate))
60 
61 static GNCSearchCoreTypeClass *parent_class;
62 
63 GType
64 gnc_search_string_get_type (void)
65 {
66  static GType type = 0;
67 
68  if (!type)
69  {
70  GTypeInfo type_info =
71  {
72  sizeof(GNCSearchStringClass), /* class_size */
73  NULL, /* base_init */
74  NULL, /* base_finalize */
75  (GClassInitFunc)gnc_search_string_class_init,
76  NULL, /* class_finalize */
77  NULL, /* class_data */
78  sizeof(GNCSearchString), /* */
79  0, /* n_preallocs */
80  (GInstanceInitFunc)gnc_search_string_init,
81  };
82 
83  type = g_type_register_static (GNC_TYPE_SEARCH_CORE_TYPE,
84  "GNCSearchString",
85  &type_info, 0);
86  }
87 
88  return type;
89 }
90 
91 static void
92 gnc_search_string_class_init (GNCSearchStringClass *klass)
93 {
94  GObjectClass *object_class;
95  GNCSearchCoreTypeClass *gnc_search_core_type = (GNCSearchCoreTypeClass *)klass;
96 
97  object_class = G_OBJECT_CLASS (klass);
98  parent_class = g_type_class_peek_parent (klass);
99 
100  object_class->finalize = gnc_search_string_finalize;
101 
102  /* override methods */
103  gnc_search_core_type->editable_enters = editable_enters;
104  gnc_search_core_type->grab_focus = grab_focus;
105  gnc_search_core_type->validate = gncs_validate;
106  gnc_search_core_type->get_widget = gncs_get_widget;
107  gnc_search_core_type->get_predicate = gncs_get_predicate;
108  gnc_search_core_type->clone = gncs_clone;
109 
110  g_type_class_add_private(klass, sizeof(GNCSearchStringPrivate));
111 }
112 
113 static void
114 gnc_search_string_init (GNCSearchString *o)
115 {
116  o->value = NULL;
117  o->how = SEARCH_STRING_CONTAINS;
118  o->ign_case = TRUE;
119 }
120 
121 static void
122 gnc_search_string_finalize (GObject *obj)
123 {
124  GNCSearchString *o = (GNCSearchString *)obj;
125  g_assert (IS_GNCSEARCH_STRING (o));
126 
127  g_free (o->value);
128 
129  G_OBJECT_CLASS (parent_class)->finalize(obj);
130 }
131 
140 gnc_search_string_new (void)
141 {
142  GNCSearchString *o = g_object_new(GNC_TYPE_SEARCH_STRING, NULL);
143  return o;
144 }
145 
146 void
147 gnc_search_string_set_value (GNCSearchString *fi, const char *value)
148 {
149  g_return_if_fail (fi);
150  g_return_if_fail (IS_GNCSEARCH_STRING (fi));
151 
152  if (fi->value)
153  g_free (fi->value);
154 
155  fi->value = g_strdup (value);
156 }
157 
158 void
159 gnc_search_string_set_how (GNCSearchString *fi, GNCSearchString_Type how)
160 {
161  g_return_if_fail (fi);
162  g_return_if_fail (IS_GNCSEARCH_STRING (fi));
163  fi->how = how;
164 }
165 
166 void
167 gnc_search_string_set_case (GNCSearchString *fi, gboolean ignore_case)
168 {
169  g_return_if_fail (fi);
170  g_return_if_fail (IS_GNCSEARCH_STRING (fi));
171  fi->ign_case = ignore_case;
172 }
173 
174 static gboolean
175 gncs_validate (GNCSearchCoreType *fe)
176 {
177  GNCSearchString *fi = (GNCSearchString *)fe;
178  gboolean valid = TRUE;
179 
180  g_return_val_if_fail (fi, FALSE);
181  g_return_val_if_fail (IS_GNCSEARCH_STRING (fi), FALSE);
182 
183  if (!fi->value || *(fi->value) == '\0')
184  {
185  GtkWidget *dialog;
186  dialog = gtk_message_dialog_new (NULL,
187  GTK_DIALOG_MODAL,
188  GTK_MESSAGE_ERROR,
189  GTK_BUTTONS_OK,
190  "%s",
191  _("You need to enter some search text."));
192  gtk_dialog_run (GTK_DIALOG (dialog));
193  gtk_widget_destroy(dialog);
194  return FALSE;
195  }
196 
197  if (fi->how == SEARCH_STRING_MATCHES_REGEX ||
198  fi->how == SEARCH_STRING_NOT_MATCHES_REGEX)
199  {
200  regex_t regexpat; /* regex patern */
201  gint regerr;
202  int flags = REG_EXTENDED;
203 
204  if (fi->ign_case)
205  flags |= REG_ICASE;
206 
207  regerr = regcomp (&regexpat, fi->value, flags);
208  if (regerr)
209  {
210  GtkWidget *dialog;
211  gchar *regmsg, *errmsg;
212  size_t reglen;
213 
214  /* regerror gets called twice to get the full error string
215  length to do proper posix error reporting */
216  reglen = regerror (regerr, &regexpat, 0, 0);
217  regmsg = g_malloc0 (reglen + 1);
218  regerror (regerr, &regexpat, regmsg, reglen);
219 
220  errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
221  fi->value, regmsg);
222  g_free (regmsg);
223 
224  dialog = gtk_message_dialog_new (NULL,
225  GTK_DIALOG_MODAL,
226  GTK_MESSAGE_ERROR,
227  GTK_BUTTONS_OK,
228  "%s", errmsg);
229  gtk_dialog_run (GTK_DIALOG (dialog));
230  gtk_widget_destroy(dialog);
231  g_free (errmsg);
232  valid = FALSE;
233  }
234 
235  regfree (&regexpat);
236  }
237 
238  return valid;
239 }
240 
241 static void
242 toggle_changed (GtkToggleButton *button, GNCSearchString *fe)
243 {
244  fe->ign_case = !gtk_toggle_button_get_active (button);
245 }
246 
247 static void
248 entry_changed (GtkEntry *entry, GNCSearchString *fe)
249 {
250  const char *new_str;
251 
252  new_str = gtk_entry_get_text(entry);
253  gnc_search_string_set_value (fe, new_str);
254 }
255 
256 static GtkWidget *
257 make_menu (GNCSearchCoreType *fe)
258 {
259  GNCSearchString *fi = (GNCSearchString *)fe;
260  GtkComboBox *combo;
261 
262  combo = GTK_COMBO_BOX(gnc_combo_box_new_search());
263 
264  gnc_combo_box_search_add(combo, _("contains"), SEARCH_STRING_CONTAINS);
265  gnc_combo_box_search_add(combo, _("equals"), SEARCH_STRING_EQUAL);
266  gnc_combo_box_search_add(combo, _("matches regex"),
267  SEARCH_STRING_MATCHES_REGEX);
268  gnc_combo_box_search_add(combo, _("does not match regex"),
269  SEARCH_STRING_NOT_MATCHES_REGEX);
270  gnc_combo_box_search_changed(combo, &fi->how);
271  gnc_combo_box_search_set_active(combo, fi->how ? fi->how : SEARCH_STRING_CONTAINS);
272 
273  return GTK_WIDGET(combo);
274 }
275 
276 static void
277 grab_focus (GNCSearchCoreType *fe)
278 {
279  GNCSearchString *fi = (GNCSearchString *)fe;
281 
282  g_return_if_fail (fi);
283  g_return_if_fail (IS_GNCSEARCH_STRING (fi));
284 
285  priv = _PRIVATE(fi);
286  if (priv->entry)
287  gtk_widget_grab_focus (priv->entry);
288 }
289 
290 static void
291 editable_enters (GNCSearchCoreType *fe)
292 {
293  GNCSearchString *fi = (GNCSearchString *)fe;
295 
296  g_return_if_fail (fi);
297  g_return_if_fail (IS_GNCSEARCH_STRING (fi));
298 
299  priv = _PRIVATE(fi);
300  if (priv->entry)
301  gtk_entry_set_activates_default(GTK_ENTRY (priv->entry), TRUE);
302 }
303 
304 static GtkWidget *
305 gncs_get_widget (GNCSearchCoreType *fe)
306 {
307  GtkWidget *entry, *toggle, *menu, *box;
308  GNCSearchString *fi = (GNCSearchString *)fe;
310 
311  g_return_val_if_fail (fi, NULL);
312  g_return_val_if_fail (IS_GNCSEARCH_STRING (fi), NULL);
313 
314  priv = _PRIVATE(fi);
315  box = gtk_hbox_new (FALSE, 3);
316 
317  /* Build and connect the option menu */
318  menu = make_menu (fe);
319  gtk_box_pack_start (GTK_BOX (box), menu, FALSE, FALSE, 3);
320 
321  /* Build and connect the entry window */
322  entry = gtk_entry_new ();
323  if (fi->value)
324  gtk_entry_set_text (GTK_ENTRY (entry), fi->value);
325  g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (entry_changed), fe);
326  gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 3);
327  priv->entry = entry;
328 
329  /* Build and connect the case-sensitive check button; defaults to off */
330  toggle = gtk_check_button_new_with_label (_("Match case"));
331  g_signal_connect (G_OBJECT(toggle), "toggled", G_CALLBACK (toggle_changed), fe);
332  gtk_box_pack_start (GTK_BOX (box), toggle, FALSE, FALSE, 3);
333 
334  /* And return the box */
335  return box;
336 }
337 
338 static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe)
339 {
340  GNCSearchString *ss = (GNCSearchString *)fe;
341  QofQueryCompare how;
342  QofStringMatch options = QOF_STRING_MATCH_NORMAL;
343  gboolean is_regex = FALSE;
344 
345  g_return_val_if_fail (ss, NULL);
346  g_return_val_if_fail (IS_GNCSEARCH_STRING (ss), NULL);
347 
348  switch (ss->how)
349  {
350  case SEARCH_STRING_MATCHES_REGEX:
351  is_regex = TRUE;
352  /* FALL THROUGH */
353  case SEARCH_STRING_CONTAINS:
354  how = QOF_COMPARE_CONTAINS;
355  break;
356  case SEARCH_STRING_EQUAL:
357  how = QOF_COMPARE_EQUAL;
358  break;
359  case SEARCH_STRING_NOT_MATCHES_REGEX:
360  is_regex = TRUE;
361  /* FALL THROUGH */
362  case SEARCH_STRING_NOT_CONTAINS:
363  how = QOF_COMPARE_NCONTAINS;
364  break;
365  case SEARCH_STRING_NOT_EQUAL:
366  how = QOF_COMPARE_NEQ;
367  break;
368  default:
369  g_warning ("invalid string choice: %d", ss->how);
370  return NULL;
371  }
372 
373  if (ss->ign_case)
374  options = QOF_STRING_MATCH_CASEINSENSITIVE;
375 
376  return qof_query_string_predicate (how, ss->value, options, is_regex);
377 }
378 
379 static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe)
380 {
381  GNCSearchString *se, *fse = (GNCSearchString *)fe;
382 
383  g_return_val_if_fail (fse, NULL);
384  g_return_val_if_fail (IS_GNCSEARCH_STRING (fse), NULL);
385 
386  se = gnc_search_string_new ();
387  gnc_search_string_set_value (se, fse->value);
388  gnc_search_string_set_how (se, fse->how);
389  gnc_search_string_set_case (se, fse->ign_case);
390 
391  return (GNCSearchCoreType *)se;
392 }
QofStringMatch
Definition: qofquerycore.h:71
QofQueryCompare
Definition: qofquerycore.h:55