gnucash/libgnucash/app-utils/QuickFill.c
Geert Janssens 83d14e1c1c Restructure the src directory
It is split into
- /libgnucash (for the non-gui bits)
- /gnucash (for the gui)
- /common (misc source files used by both)
- /bindings (currently only holds python bindings)

This is the first step in restructuring the code. It will need much
more fine tuning later on.
2017-08-10 18:45:00 +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 ********************************* \
\********************************************************************/