mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Typos found via `codespell -q 3 -D ~/Projects/codespell/codespell_lib/data/dictionary.txt -S *.po,./po,*.min.js,./ChangeLog*,./NEWS,./doc/README*,./AUTHORS,./libgnucash/tax/us/txf-de*,./data/accounts -L ans,cas,dragable,gae,iff,iif,mut,nd,numer,startd,stoll`
385 lines
11 KiB
C
385 lines
11 KiB
C
/*
|
|
* Copyright (C) 2002 Derek Atkins
|
|
*
|
|
* Authors: Derek Atkins <warlord@MIT.EDU>
|
|
*
|
|
* Copyright (c) 2006 David Hampton <hampton@employees.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib/gi18n.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <regex.h>
|
|
|
|
#include "search-string.h"
|
|
#include "search-core-utils.h"
|
|
#include "qof.h"
|
|
|
|
#define d(x)
|
|
|
|
static void editable_enters (GNCSearchCoreType *fe);
|
|
static void pass_parent (GNCSearchCoreType *fe, gpointer parent);
|
|
static void grab_focus (GNCSearchCoreType *fe);
|
|
static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe);
|
|
static gboolean gncs_validate (GNCSearchCoreType *fe);
|
|
static GtkWidget *gncs_get_widget(GNCSearchCoreType *fe);
|
|
static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe);
|
|
|
|
static void gnc_search_string_class_init (GNCSearchStringClass *klass);
|
|
static void gnc_search_string_init (GNCSearchString *gspaper);
|
|
static void gnc_search_string_finalize (GObject *obj);
|
|
|
|
typedef struct _GNCSearchStringPrivate GNCSearchStringPrivate;
|
|
|
|
struct _GNCSearchStringPrivate
|
|
{
|
|
GtkWidget *entry;
|
|
GtkWindow *parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(GNCSearchString, gnc_search_string, GNC_TYPE_SEARCH_CORE_TYPE)
|
|
|
|
#define _PRIVATE(o) \
|
|
((GNCSearchStringPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_SEARCH_STRING))
|
|
|
|
static GNCSearchCoreTypeClass *parent_class;
|
|
|
|
static void
|
|
gnc_search_string_class_init (GNCSearchStringClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
GNCSearchCoreTypeClass *gnc_search_core_type = (GNCSearchCoreTypeClass *)klass;
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
object_class->finalize = gnc_search_string_finalize;
|
|
|
|
/* override methods */
|
|
gnc_search_core_type->editable_enters = editable_enters;
|
|
gnc_search_core_type->pass_parent = pass_parent;
|
|
gnc_search_core_type->grab_focus = grab_focus;
|
|
gnc_search_core_type->validate = gncs_validate;
|
|
gnc_search_core_type->get_widget = gncs_get_widget;
|
|
gnc_search_core_type->get_predicate = gncs_get_predicate;
|
|
gnc_search_core_type->clone = gncs_clone;
|
|
}
|
|
|
|
static void
|
|
gnc_search_string_init (GNCSearchString *o)
|
|
{
|
|
o->value = NULL;
|
|
o->how = SEARCH_STRING_CONTAINS;
|
|
o->ign_case = TRUE;
|
|
}
|
|
|
|
static void
|
|
gnc_search_string_finalize (GObject *obj)
|
|
{
|
|
GNCSearchString *o = (GNCSearchString *)obj;
|
|
g_assert (IS_GNCSEARCH_STRING (o));
|
|
|
|
g_free (o->value);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize(obj);
|
|
}
|
|
|
|
/**
|
|
* gnc_search_string_new:
|
|
*
|
|
* Create a new GNCSearchString object.
|
|
*
|
|
* Return value: A new #GNCSearchString object.
|
|
**/
|
|
GNCSearchString *
|
|
gnc_search_string_new (void)
|
|
{
|
|
GNCSearchString *o = g_object_new(GNC_TYPE_SEARCH_STRING, NULL);
|
|
return o;
|
|
}
|
|
|
|
void
|
|
gnc_search_string_set_value (GNCSearchString *fi, const char *value)
|
|
{
|
|
g_return_if_fail (fi);
|
|
g_return_if_fail (IS_GNCSEARCH_STRING (fi));
|
|
|
|
if (fi->value)
|
|
g_free (fi->value);
|
|
|
|
fi->value = g_strdup (value);
|
|
}
|
|
|
|
void
|
|
gnc_search_string_set_how (GNCSearchString *fi, GNCSearchString_Type how)
|
|
{
|
|
g_return_if_fail (fi);
|
|
g_return_if_fail (IS_GNCSEARCH_STRING (fi));
|
|
fi->how = how;
|
|
}
|
|
|
|
void
|
|
gnc_search_string_set_case (GNCSearchString *fi, gboolean ignore_case)
|
|
{
|
|
g_return_if_fail (fi);
|
|
g_return_if_fail (IS_GNCSEARCH_STRING (fi));
|
|
fi->ign_case = ignore_case;
|
|
}
|
|
|
|
static gboolean
|
|
gncs_validate (GNCSearchCoreType *fe)
|
|
{
|
|
GNCSearchString *fi = (GNCSearchString *)fe;
|
|
GNCSearchStringPrivate *priv;
|
|
gboolean valid = TRUE;
|
|
|
|
g_return_val_if_fail (fi, FALSE);
|
|
g_return_val_if_fail (IS_GNCSEARCH_STRING (fi), FALSE);
|
|
|
|
priv = _PRIVATE(fi);
|
|
|
|
if (!fi->value || *(fi->value) == '\0')
|
|
{
|
|
GtkWidget *dialog;
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW(priv->parent),
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_OK,
|
|
"%s",
|
|
_("You need to enter some search text."));
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy(dialog);
|
|
return FALSE;
|
|
}
|
|
|
|
if (fi->how == SEARCH_STRING_MATCHES_REGEX ||
|
|
fi->how == SEARCH_STRING_NOT_MATCHES_REGEX)
|
|
{
|
|
regex_t regexpat; /* regex pattern */
|
|
gint regerr;
|
|
int flags = REG_EXTENDED;
|
|
|
|
if (fi->ign_case)
|
|
flags |= REG_ICASE;
|
|
|
|
regerr = regcomp (®expat, fi->value, flags);
|
|
if (regerr)
|
|
{
|
|
GtkWidget *dialog;
|
|
gchar *regmsg, *errmsg;
|
|
size_t reglen;
|
|
|
|
/* regerror gets called twice to get the full error string
|
|
length to do proper posix error reporting */
|
|
reglen = regerror (regerr, ®expat, 0, 0);
|
|
regmsg = g_malloc0 (reglen + 1);
|
|
regerror (regerr, ®expat, regmsg, reglen);
|
|
|
|
errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
|
|
fi->value, regmsg);
|
|
g_free (regmsg);
|
|
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW(priv->parent),
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_OK,
|
|
"%s", errmsg);
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy(dialog);
|
|
g_free (errmsg);
|
|
valid = FALSE;
|
|
}
|
|
|
|
regfree (®expat);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
static void
|
|
toggle_changed (GtkToggleButton *button, GNCSearchString *fe)
|
|
{
|
|
fe->ign_case = !gtk_toggle_button_get_active (button);
|
|
}
|
|
|
|
static void
|
|
entry_changed (GtkEntry *entry, GNCSearchString *fe)
|
|
{
|
|
const char *new_str;
|
|
|
|
new_str = gtk_entry_get_text(entry);
|
|
gnc_search_string_set_value (fe, new_str);
|
|
}
|
|
|
|
static GtkWidget *
|
|
make_menu (GNCSearchCoreType *fe)
|
|
{
|
|
GNCSearchString *fi = (GNCSearchString *)fe;
|
|
GtkComboBox *combo;
|
|
|
|
combo = GTK_COMBO_BOX(gnc_combo_box_new_search());
|
|
|
|
gnc_combo_box_search_add(combo, _("contains"), SEARCH_STRING_CONTAINS);
|
|
gnc_combo_box_search_add(combo, _("equals"), SEARCH_STRING_EQUAL);
|
|
gnc_combo_box_search_add(combo, _("matches regex"),
|
|
SEARCH_STRING_MATCHES_REGEX);
|
|
gnc_combo_box_search_add(combo, _("does not match regex"),
|
|
SEARCH_STRING_NOT_MATCHES_REGEX);
|
|
gnc_combo_box_search_changed(combo, &fi->how);
|
|
gnc_combo_box_search_set_active(combo, fi->how ? fi->how : SEARCH_STRING_CONTAINS);
|
|
|
|
return GTK_WIDGET(combo);
|
|
}
|
|
|
|
static void
|
|
grab_focus (GNCSearchCoreType *fe)
|
|
{
|
|
GNCSearchString *fi = (GNCSearchString *)fe;
|
|
GNCSearchStringPrivate *priv;
|
|
|
|
g_return_if_fail (fi);
|
|
g_return_if_fail (IS_GNCSEARCH_STRING (fi));
|
|
|
|
priv = _PRIVATE(fi);
|
|
if (priv->entry)
|
|
gtk_widget_grab_focus (priv->entry);
|
|
}
|
|
|
|
static void
|
|
editable_enters (GNCSearchCoreType *fe)
|
|
{
|
|
GNCSearchString *fi = (GNCSearchString *)fe;
|
|
GNCSearchStringPrivate *priv;
|
|
|
|
g_return_if_fail (fi);
|
|
g_return_if_fail (IS_GNCSEARCH_STRING (fi));
|
|
|
|
priv = _PRIVATE(fi);
|
|
if (priv->entry)
|
|
gtk_entry_set_activates_default(GTK_ENTRY (priv->entry), TRUE);
|
|
}
|
|
|
|
static void
|
|
pass_parent (GNCSearchCoreType *fe, gpointer parent)
|
|
{
|
|
GNCSearchString *fi = (GNCSearchString *)fe;
|
|
GNCSearchStringPrivate *priv;
|
|
|
|
g_return_if_fail (fi);
|
|
g_return_if_fail (IS_GNCSEARCH_STRING (fi));
|
|
|
|
priv = _PRIVATE(fi);
|
|
priv->parent = GTK_WINDOW(parent);
|
|
}
|
|
|
|
static GtkWidget *
|
|
gncs_get_widget (GNCSearchCoreType *fe)
|
|
{
|
|
GtkWidget *entry, *toggle, *menu, *box;
|
|
GNCSearchString *fi = (GNCSearchString *)fe;
|
|
GNCSearchStringPrivate *priv;
|
|
|
|
g_return_val_if_fail (fi, NULL);
|
|
g_return_val_if_fail (IS_GNCSEARCH_STRING (fi), NULL);
|
|
|
|
priv = _PRIVATE(fi);
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
|
|
gtk_box_set_homogeneous (GTK_BOX (box), FALSE);
|
|
|
|
/* Build and connect the option menu */
|
|
menu = make_menu (fe);
|
|
gtk_box_pack_start (GTK_BOX (box), menu, FALSE, FALSE, 3);
|
|
|
|
/* Build and connect the entry window */
|
|
entry = gtk_entry_new ();
|
|
if (fi->value)
|
|
gtk_entry_set_text (GTK_ENTRY (entry), fi->value);
|
|
g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (entry_changed), fe);
|
|
gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 3);
|
|
priv->entry = entry;
|
|
|
|
/* Build and connect the case-sensitive check button; defaults to off */
|
|
toggle = gtk_check_button_new_with_label (_("Match case"));
|
|
g_signal_connect (G_OBJECT(toggle), "toggled", G_CALLBACK (toggle_changed), fe);
|
|
gtk_box_pack_start (GTK_BOX (box), toggle, FALSE, FALSE, 3);
|
|
|
|
/* And return the box */
|
|
return box;
|
|
}
|
|
|
|
static QofQueryPredData* gncs_get_predicate (GNCSearchCoreType *fe)
|
|
{
|
|
GNCSearchString *ss = (GNCSearchString *)fe;
|
|
QofQueryCompare how;
|
|
QofStringMatch options = QOF_STRING_MATCH_NORMAL;
|
|
gboolean is_regex = FALSE;
|
|
|
|
g_return_val_if_fail (ss, NULL);
|
|
g_return_val_if_fail (IS_GNCSEARCH_STRING (ss), NULL);
|
|
|
|
switch (ss->how)
|
|
{
|
|
case SEARCH_STRING_MATCHES_REGEX:
|
|
is_regex = TRUE;
|
|
/* FALL THROUGH */
|
|
case SEARCH_STRING_CONTAINS:
|
|
how = QOF_COMPARE_CONTAINS;
|
|
break;
|
|
case SEARCH_STRING_EQUAL:
|
|
how = QOF_COMPARE_EQUAL;
|
|
break;
|
|
case SEARCH_STRING_NOT_MATCHES_REGEX:
|
|
is_regex = TRUE;
|
|
/* FALL THROUGH */
|
|
case SEARCH_STRING_NOT_CONTAINS:
|
|
how = QOF_COMPARE_NCONTAINS;
|
|
break;
|
|
case SEARCH_STRING_NOT_EQUAL:
|
|
how = QOF_COMPARE_NEQ;
|
|
break;
|
|
default:
|
|
g_warning ("invalid string choice: %d", ss->how);
|
|
return NULL;
|
|
}
|
|
|
|
if (ss->ign_case)
|
|
options = QOF_STRING_MATCH_CASEINSENSITIVE;
|
|
|
|
return qof_query_string_predicate (how, ss->value, options, is_regex);
|
|
}
|
|
|
|
static GNCSearchCoreType *gncs_clone(GNCSearchCoreType *fe)
|
|
{
|
|
GNCSearchString *se, *fse = (GNCSearchString *)fe;
|
|
|
|
g_return_val_if_fail (fse, NULL);
|
|
g_return_val_if_fail (IS_GNCSEARCH_STRING (fse), NULL);
|
|
|
|
se = gnc_search_string_new ();
|
|
gnc_search_string_set_value (se, fse->value);
|
|
gnc_search_string_set_how (se, fse->how);
|
|
gnc_search_string_set_case (se, fse->ign_case);
|
|
|
|
return (GNCSearchCoreType *)se;
|
|
}
|