gnucash/libgnucash/app-utils/QuickFill.c
Geert Janssens 1238b9d8cd Prevent gcc from searching config.h in the current directory
This will avoid a ninja-build from picking up a config.h generated by the autotools build
(in the root build directory). Picking up the wrong config.h may lead to all kinds of
subtle issues if the autotools run was done with different options than the cmake run.
2017-10-26 14:05:17 +02:00

452 lines
12 KiB
C

/********************************************************************\
* QuickFill.h -- the quickfill tree data structure *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998 Linas Vepstas *
* Copyright (C) 2000 Dave Peticolas *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
\********************************************************************/
#include <config.h>
#include <string.h>
#include "QuickFill.h"
#include "gnc-engine.h"
#include "gnc-ui-util.h"
struct _QuickFill
{
char *text; /* the first matching text string */
int len; /* number of chars in text string */
GHashTable *matches; /* array of children in the tree */
};
/** PROTOTYPES ******************************************************/
static void quickfill_insert_recursive (QuickFill *qf, const char *text, int len,
const char* next_char, QuickFillSort sort);
static void gnc_quickfill_remove_recursive (QuickFill *qf, const gchar *text,
gint depth, QuickFillSort sort);
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_REGISTER;
/********************************************************************\
\********************************************************************/
QuickFill *
gnc_quickfill_new (void)
{
QuickFill *qf;
if (sizeof (guint) < sizeof (gunichar))
{
PWARN ("Can't use quickfill");
return NULL;
}
qf = g_new (QuickFill, 1);
qf->text = NULL;
qf->len = 0;
qf->matches = g_hash_table_new (g_direct_hash, g_direct_equal);
return qf;
}
/********************************************************************\
\********************************************************************/
static gboolean
destroy_helper (gpointer key, gpointer value, gpointer data)
{
gnc_quickfill_destroy (value);
return TRUE;
}
void
gnc_quickfill_destroy (QuickFill *qf)
{
if (qf == NULL)
return;
g_hash_table_foreach (qf->matches, (GHFunc)destroy_helper, NULL);
g_hash_table_destroy (qf->matches);
qf->matches = NULL;
if (qf->text)
g_free(qf->text);
qf->text = NULL;
qf->len = 0;
g_free (qf);
}
void
gnc_quickfill_purge (QuickFill *qf)
{
if (qf == NULL)
return;
g_hash_table_foreach_remove (qf->matches, destroy_helper, NULL);
if (qf->text)
g_free (qf->text);
qf->text = NULL;
qf->len = 0;
}
/********************************************************************\
\********************************************************************/
const char *
gnc_quickfill_string (QuickFill *qf)
{
if (qf == NULL)
return NULL;
return qf->text;
}
/********************************************************************\
\********************************************************************/
QuickFill *
gnc_quickfill_get_char_match (QuickFill *qf, gunichar uc)
{
guint key = g_unichar_toupper (uc);
if (NULL == qf) return NULL;
DEBUG ("xaccGetQuickFill(): index = %u\n", key);
return g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
}
/********************************************************************\
\********************************************************************/
QuickFill *
gnc_quickfill_get_string_len_match (QuickFill *qf,
const char *str, int len)
{
const char *c;
gunichar uc;
if (NULL == qf) return NULL;
if (NULL == str) return NULL;
c = str;
while (*c && (len > 0))
{
if (qf == NULL)
return NULL;
uc = g_utf8_get_char (c);
qf = gnc_quickfill_get_char_match (qf, uc);
c = g_utf8_next_char (c);
len--;
}
return qf;
}
/********************************************************************\
\********************************************************************/
QuickFill *
gnc_quickfill_get_string_match (QuickFill *qf, const char *str)
{
if (NULL == qf) return NULL;
if (NULL == str) return NULL;
return gnc_quickfill_get_string_len_match (qf, str, g_utf8_strlen (str, -1));
}
/********************************************************************\
\********************************************************************/
static void
unique_len_helper (gpointer key, gpointer value, gpointer data)
{
QuickFill **qf_p = data;
*qf_p = value;
}
QuickFill *
gnc_quickfill_get_unique_len_match (QuickFill *qf, int *length)
{
if (length != NULL)
*length = 0;
if (qf == NULL)
return NULL;
while (1)
{
guint count;
count = g_hash_table_size (qf->matches);
if (count != 1)
break;
g_hash_table_foreach (qf->matches, unique_len_helper, &qf);
if (length != NULL)
(*length)++;
}
return qf;
}
/********************************************************************\
\********************************************************************/
void
gnc_quickfill_insert (QuickFill *qf, const char *text, QuickFillSort sort)
{
gchar *normalized_str;
int len;
if (NULL == qf) return;
if (NULL == text) return;
normalized_str = g_utf8_normalize (text, -1, G_NORMALIZE_NFC);
len = g_utf8_strlen (text, -1);
quickfill_insert_recursive (qf, normalized_str, len, normalized_str, sort);
g_free (normalized_str);
}
/********************************************************************\
\********************************************************************/
static void
quickfill_insert_recursive (QuickFill *qf, const char *text, int len,
const char *next_char, QuickFillSort sort)
{
guint key;
char *old_text;
QuickFill *match_qf;
gunichar key_char_uc;
if (qf == NULL)
return;
if ((text == NULL) || (*next_char == '\0'))
return;
key_char_uc = g_utf8_get_char (next_char);
key = g_unichar_toupper (key_char_uc);
match_qf = g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
if (match_qf == NULL)
{
match_qf = gnc_quickfill_new ();
g_hash_table_insert (qf->matches, GUINT_TO_POINTER (key), match_qf);
}
old_text = match_qf->text;
switch (sort)
{
case QUICKFILL_ALPHA:
if (old_text && (g_utf8_collate (text, old_text) >= 0))
break;
/* fall through */
case QUICKFILL_LIFO:
default:
/* If there's no string there already, just put the new one in. */
if (old_text == NULL)
{
match_qf->text = g_strdup(text);
match_qf->len = len;
break;
}
/* Leave prefixes in place */
if ((len > match_qf->len) &&
(strncmp(text, old_text, strlen(old_text)) == 0))
break;
g_free(old_text);
match_qf->text = g_strdup(text);
match_qf->len = len;
break;
}
quickfill_insert_recursive (match_qf, text, len, g_utf8_next_char (next_char), sort);
}
/********************************************************************\
\********************************************************************/
void
gnc_quickfill_remove (QuickFill *qf, const gchar *text, QuickFillSort sort)
{
gchar *normalized_str;
if (qf == NULL) return;
if (text == NULL) return;
normalized_str = g_utf8_normalize (text, -1, G_NORMALIZE_NFC);
gnc_quickfill_remove_recursive (qf, normalized_str, 0, sort);
g_free (normalized_str);
}
/********************************************************************\
\********************************************************************/
struct _BestText
{
gchar *text;
QuickFillSort sort;
};
static void
best_text_helper (gpointer key, gpointer value, gpointer user_data)
{
QuickFill *qf = value;
struct _BestText *best = user_data;
if (best->text == NULL)
{
/* start with the first text */
best->text = qf->text;
}
else if (best->text == QUICKFILL_LIFO)
{
/* we do not track history, so ignore it */
return;
}
else if (g_utf8_collate (qf->text, best->text) < 0)
{
/* even better text */
best->text = qf->text;
}
}
static void
gnc_quickfill_remove_recursive (QuickFill *qf, const gchar *text, gint depth,
QuickFillSort sort)
{
QuickFill *match_qf;
gchar *child_text;
gint child_len;
child_text = NULL;
child_len = 0;
if (depth < g_utf8_strlen (text, -1))
{
/* process next letter */
gchar *key_char;
gunichar key_char_uc;
guint key;
key_char = g_utf8_offset_to_pointer (text, depth);
key_char_uc = g_utf8_get_char (key_char);
key = g_unichar_toupper (key_char_uc);
match_qf = g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
if (match_qf)
{
/* remove text from child qf */
gnc_quickfill_remove_recursive (match_qf, text, depth + 1, sort);
if (match_qf->text == NULL)
{
/* text was the only word with a prefix up to match_qf */
g_hash_table_remove (qf->matches, GUINT_TO_POINTER (key));
gnc_quickfill_destroy (match_qf);
}
else
{
/* remember remaining best child string */
child_text = match_qf->text;
child_len = match_qf->len;
}
}
}
if (qf->text == NULL)
return;
if (strcmp (text, qf->text) == 0)
{
/* the currently best text is about to be removed */
gchar *best_text = NULL;
gint best_len = 0;
if (child_text != NULL)
{
/* other children are pretty good as well */
best_text = child_text;
best_len = child_len;
}
else
{
if (g_hash_table_size (qf->matches) != 0)
{
/* otherwise search for another good text */
struct _BestText bts;
bts.text = NULL;
bts.sort = sort;
g_hash_table_foreach (qf->matches, (GHFunc) best_text_helper, &bts);
best_text = bts.text;
best_len = (best_text == NULL) ? 0 : g_utf8_strlen (best_text, -1);
}
}
/* now replace or clear text */
g_free(qf->text);
if (best_text != NULL)
{
qf->text = g_strdup(best_text);
qf->len = best_len;
}
else
{
qf->text = NULL;
qf->len = 0;
}
}
}
/********************** END OF FILE ********************************* \
\********************************************************************/