aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--widgets/text/e-completion-match.c11
-rw-r--r--widgets/text/e-completion-match.h1
-rw-r--r--widgets/text/e-completion-test.c6
-rw-r--r--widgets/text/e-completion.c211
-rw-r--r--widgets/text/e-completion.h23
5 files changed, 232 insertions, 20 deletions
diff --git a/widgets/text/e-completion-match.c b/widgets/text/e-completion-match.c
index 6e7863f887..c14c8fe40a 100644
--- a/widgets/text/e-completion-match.c
+++ b/widgets/text/e-completion-match.c
@@ -48,13 +48,14 @@ e_completion_match_construct (ECompletionMatch *match)
g_return_if_fail (match != NULL);
match->match_text = NULL;
- match->menu_text = NULL;
- match->score = 0;
+ match->menu_text = NULL;
+ match->score = 0;
match->sort_major = 0;
match->sort_minor = 0;
- match->user_data = NULL;
- match->ref = 1;
- match->destroy = NULL;
+ match->user_data = NULL;
+ match->ref = 1;
+ match->hit_count = 0;
+ match->destroy = NULL;
}
void
diff --git a/widgets/text/e-completion-match.h b/widgets/text/e-completion-match.h
index 4ec1733aca..c7e2a698bb 100644
--- a/widgets/text/e-completion-match.h
+++ b/widgets/text/e-completion-match.h
@@ -45,6 +45,7 @@ struct _ECompletionMatch {
gpointer user_data;
gint ref;
+ gint hit_count;
void (*destroy) (ECompletionMatch *);
};
diff --git a/widgets/text/e-completion-test.c b/widgets/text/e-completion-test.c
index 7a5ed920cd..cf6b925a0e 100644
--- a/widgets/text/e-completion-test.c
+++ b/widgets/text/e-completion-test.c
@@ -119,7 +119,7 @@ dict_check (gpointer ptr)
}
static void
-begin_dict_search (ECompletion *complete, const gchar *txt, gint pos, gint limit, gpointer user_data)
+request_dict_search (ECompletion *complete, const gchar *txt, gint pos, gint limit, gpointer user_data)
{
gint len = strlen (txt);
@@ -170,8 +170,8 @@ main (int argc, gchar **argv)
complete = e_completion_new ();
gtk_signal_connect (GTK_OBJECT (complete),
- "begin_completion",
- GTK_SIGNAL_FUNC (begin_dict_search),
+ "request_completion",
+ GTK_SIGNAL_FUNC (request_dict_search),
NULL);
gtk_signal_connect (GTK_OBJECT (complete),
"end_completion",
diff --git a/widgets/text/e-completion.c b/widgets/text/e-completion.c
index af3676e3ce..8c9adb4eba 100644
--- a/widgets/text/e-completion.c
+++ b/widgets/text/e-completion.c
@@ -32,6 +32,7 @@
#include "e-completion.h"
enum {
+ E_COMPLETION_REQUEST_COMPLETION,
E_COMPLETION_BEGIN_COMPLETION,
E_COMPLETION_COMPLETION,
E_COMPLETION_RESTART_COMPLETION,
@@ -46,21 +47,32 @@ static guint e_completion_signals[E_COMPLETION_LAST_SIGNAL] = { 0 };
struct _ECompletionPrivate {
gboolean searching;
+ gboolean done_search;
+ gboolean refining;
gchar *search_text;
GPtrArray *matches;
+ gint match_count;
gint pos;
gint limit;
double min_score, max_score;
+ gint refinement_count;
+ GList *search_stack;
};
+typedef struct {
+ gchar *text;
+ gint pos;
+} ECompletionSearch;
+
static void e_completion_class_init (ECompletionClass *klass);
static void e_completion_init (ECompletion *complete);
static void e_completion_destroy (GtkObject *object);
-static void e_completion_add_match (ECompletion *complete, ECompletionMatch *);
-static void e_completion_clear_matches (ECompletion *complete);
-static gboolean e_completion_sort (ECompletion *complete);
-static void e_completion_restart (ECompletion *complete);
+static void e_completion_add_match (ECompletion *complete, ECompletionMatch *);
+static void e_completion_clear_search_stack (ECompletion *complete);
+static void e_completion_clear_matches (ECompletion *complete);
+static gboolean e_completion_sort (ECompletion *complete);
+static void e_completion_restart (ECompletion *complete);
static GtkObjectClass *parent_class;
@@ -95,6 +107,15 @@ e_completion_class_init (ECompletionClass *klass)
parent_class = GTK_OBJECT_CLASS (gtk_type_class (gtk_object_get_type ()));
+ e_completion_signals[E_COMPLETION_REQUEST_COMPLETION] =
+ gtk_signal_new ("request_completion",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ECompletionClass, request_completion),
+ gtk_marshal_NONE__POINTER_INT_INT,
+ GTK_TYPE_NONE, 3,
+ GTK_TYPE_POINTER, GTK_TYPE_INT, GTK_TYPE_INT);
+
e_completion_signals[E_COMPLETION_BEGIN_COMPLETION] =
gtk_signal_new ("begin_completion",
GTK_RUN_LAST,
@@ -174,6 +195,7 @@ e_completion_destroy (GtkObject *object)
complete->priv->search_text = NULL;
e_completion_clear_matches (complete);
+ e_completion_clear_search_stack (complete);
g_ptr_array_free (complete->priv->matches, TRUE);
complete->priv->matches = NULL;
@@ -231,32 +253,193 @@ e_completion_clear (ECompletion *complete)
g_return_if_fail (E_IS_COMPLETION (complete));
/* FIXME: do we really want _clear and _clear_matches() ? */
+
+ /* I think yes, because it is convenient to be able to clear our match cache
+ without emitting a "clear_completion" signal. -JT */
+
e_completion_clear_matches (complete);
+ e_completion_clear_search_stack (complete);
+ complete->priv->refinement_count = 0;
+ complete->priv->match_count = 0;
gtk_signal_emit (GTK_OBJECT (complete), e_completion_signals[E_COMPLETION_CLEAR_COMPLETION]);
}
+static void
+e_completion_push_search (ECompletion *complete, const gchar *text, gint pos)
+{
+ ECompletionSearch *search;
+
+ g_return_if_fail (E_IS_COMPLETION (complete));
+
+ search = g_new (ECompletionSearch, 1);
+ search->text = complete->priv->search_text;
+ search->pos = complete->priv->pos;
+ complete->priv->search_stack = g_list_prepend (complete->priv->search_stack, search);
+
+ complete->priv->search_text = g_strdup (text);
+ complete->priv->pos = pos;
+}
+
+static void
+e_completion_pop_search (ECompletion *complete)
+{
+ ECompletionSearch *search;
+ GList *old_link = complete->priv->search_stack;
+
+ g_return_if_fail (E_IS_COMPLETION (complete));
+ g_return_if_fail (complete->priv->search_stack != NULL);
+
+ g_free (complete->priv->search_text);
+
+ search = complete->priv->search_stack->data;
+ complete->priv->search_text = search->text;
+ complete->priv->pos = search->pos;
+
+ g_free (search);
+ complete->priv->search_stack = g_list_remove_link (complete->priv->search_stack,
+ complete->priv->search_stack);
+ g_list_free_1 (old_link);
+}
+
+static void
+e_completion_clear_search_stack (ECompletion *complete)
+{
+ GList *iter;
+
+ g_return_if_fail (E_IS_COMPLETION (complete));
+
+ for (iter = complete->priv->search_stack; iter != NULL; iter = g_list_next (iter)) {
+ ECompletionSearch *search = iter->data;
+ g_free (search->text);
+ g_free (search);
+ }
+ g_list_free (complete->priv->search_stack);
+ complete->priv->search_stack = NULL;
+}
+
+static void
+e_completion_refine_search (ECompletion *comp, const gchar *text, gint pos, ECompletionRefineFn refine_fn)
+{
+ GPtrArray *m;
+ gint i;
+
+ comp->priv->refining = TRUE;
+
+ e_completion_push_search (comp, text, pos);
+
+ gtk_signal_emit (GTK_OBJECT (comp), e_completion_signals[E_COMPLETION_BEGIN_COMPLETION], text, pos, comp->priv->limit);
+
+ comp->priv->match_count = 0;
+
+ comp->priv->searching = TRUE;
+
+ m = comp->priv->matches;
+ for (i = 0; i < m->len; ++i) {
+ ECompletionMatch *match = g_ptr_array_index (m, i);
+ if (comp->priv->refinement_count == match->hit_count
+ && refine_fn (comp, match, text, pos)) {
+ ++match->hit_count;
+ gtk_signal_emit (GTK_OBJECT (comp), e_completion_signals[E_COMPLETION_COMPLETION], match);
+ ++comp->priv->match_count;
+ }
+ }
+
+ ++comp->priv->refinement_count;
+
+ gtk_signal_emit (GTK_OBJECT (comp), e_completion_signals[E_COMPLETION_END_COMPLETION]);
+
+ comp->priv->searching = FALSE;
+ comp->priv->refining = FALSE;
+}
+
+static void
+e_completion_unrefine_search (ECompletion *comp)
+{
+ GPtrArray *m;
+ gint i;
+
+ comp->priv->refining = TRUE;
+
+ e_completion_pop_search (comp);
+
+ gtk_signal_emit (GTK_OBJECT (comp), e_completion_signals[E_COMPLETION_BEGIN_COMPLETION], comp->priv->search_text, comp->priv->pos, comp->priv->limit);
+
+ comp->priv->match_count = 0;
+ --comp->priv->refinement_count;
+
+ comp->priv->searching = TRUE;
+
+ m = comp->priv->matches;
+ for (i = 0; i < m->len; ++i) {
+ ECompletionMatch *match = g_ptr_array_index (m, i);
+ if (comp->priv->refinement_count <= match->hit_count) {
+ match->hit_count = comp->priv->refinement_count;
+ gtk_signal_emit (GTK_OBJECT (comp), e_completion_signals[E_COMPLETION_COMPLETION], match);
+ ++comp->priv->match_count;
+ }
+ }
+
+ gtk_signal_emit (GTK_OBJECT (comp), e_completion_signals[E_COMPLETION_END_COMPLETION]);
+
+ comp->priv->searching = FALSE;
+ comp->priv->refining = FALSE;
+}
+
void
e_completion_begin_search (ECompletion *complete, const gchar *text, gint pos, gint limit)
{
+ ECompletionClass *klass;
+ ECompletionRefineFn refine_fn;
+
g_return_if_fail (complete != NULL);
g_return_if_fail (E_IS_COMPLETION (complete));
g_return_if_fail (text != NULL);
+ klass = E_COMPLETION_CLASS (GTK_OBJECT (complete)->klass);
+
+ if (!complete->priv->searching && complete->priv->done_search) {
+
+ /* If the search we are requesting is the same as what we had before our last refinement,
+ treat the request as an unrefine. */
+ if (complete->priv->search_stack != NULL) {
+ ECompletionSearch *search = complete->priv->search_stack->data;
+ if ((klass->ignore_pos_on_auto_unrefine || search->pos == pos)
+ && !strcmp (search->text, text)) {
+ e_completion_unrefine_search (complete);
+ return;
+ }
+ }
+
+ if (klass->auto_refine
+ && (refine_fn = klass->auto_refine (complete,
+ complete->priv->search_text, complete->priv->pos,
+ text, pos))) {
+ e_completion_refine_search (complete, text, pos, refine_fn);
+ return;
+ }
+
+ }
+
/* Stop any prior search. */
if (complete->priv->searching)
e_completion_cancel_search (complete);
+ e_completion_clear_search_stack (complete);
+
g_free (complete->priv->search_text);
complete->priv->search_text = g_strdup (text);
complete->priv->pos = pos;
complete->priv->searching = TRUE;
+ complete->priv->done_search = FALSE;
e_completion_clear_matches (complete);
complete->priv->limit = limit > 0 ? limit : G_MAXINT;
+ complete->priv->refinement_count = 0;
gtk_signal_emit (GTK_OBJECT (complete), e_completion_signals[E_COMPLETION_BEGIN_COMPLETION], text, pos, limit);
+ gtk_signal_emit (GTK_OBJECT (complete), e_completion_signals[E_COMPLETION_REQUEST_COMPLETION], text, pos, limit);
}
void
@@ -283,6 +466,15 @@ e_completion_searching (ECompletion *complete)
return complete->priv->searching;
}
+gboolean
+e_completion_refining (ECompletion *complete)
+{
+ g_return_val_if_fail (complete != NULL, FALSE);
+ g_return_val_if_fail (E_IS_COMPLETION (complete), FALSE);
+
+ return complete->priv->refining;
+}
+
const gchar *
e_completion_search_text (ECompletion *complete)
{
@@ -307,7 +499,7 @@ e_completion_match_count (ECompletion *complete)
g_return_val_if_fail (complete != NULL, 0);
g_return_val_if_fail (E_IS_COMPLETION (complete), 0);
- return complete->priv->matches->len;
+ return complete->priv->refinement_count > 0 ? complete->priv->match_count : complete->priv->matches->len;
}
void
@@ -323,8 +515,12 @@ e_completion_foreach_match (ECompletion *complete, ECompletionMatchFn fn, gpoint
return;
m = complete->priv->matches;
- for (i = 0; i < m->len; i++)
- fn (g_ptr_array_index (m, i), closure);
+ for (i = 0; i < m->len; i++) {
+ ECompletionMatch *match = g_ptr_array_index (m, i);
+ if (match->hit_count == complete->priv->refinement_count) {
+ fn (match, closure);
+ }
+ }
}
ECompletion *
@@ -445,5 +641,6 @@ e_completion_end_search (ECompletion *complete)
gtk_signal_emit (GTK_OBJECT (complete), e_completion_signals[E_COMPLETION_END_COMPLETION]);
complete->priv->searching = FALSE;
+ complete->priv->done_search = TRUE;
}
diff --git a/widgets/text/e-completion.h b/widgets/text/e-completion.h
index 522876730c..9340e47716 100644
--- a/widgets/text/e-completion.h
+++ b/widgets/text/e-completion.h
@@ -44,6 +44,8 @@ typedef struct _ECompletion ECompletion;
typedef struct _ECompletionClass ECompletionClass;
struct _ECompletionPrivate;
+typedef gboolean (*ECompletionRefineFn) (ECompletion *, ECompletionMatch *, const gchar *search_text, gint pos);
+
struct _ECompletion {
GtkObject parent;
@@ -53,22 +55,33 @@ struct _ECompletion {
struct _ECompletionClass {
GtkObjectClass parent_class;
+ /* virtual functions */
+ ECompletionRefineFn (*auto_refine) (ECompletion *comp,
+ const gchar *old_text, gint old_pos,
+ const gchar *new_text, gint new_pos);
+ gboolean ignore_pos_on_auto_unrefine;
+
/* Signals */
- void (*begin_completion) (ECompletion *comp, const gchar *search_text, gint pos, gint limit);
+ void (*request_completion) (ECompletion *comp, const gchar *search_text, gint pos, gint limit);
+
+ void (*begin_completion) (ECompletion *comp, const gchar *search_text, gint pos, gint limit);
+ void (*restart_completion) (ECompletion *comp);
+
void (*completion) (ECompletion *comp, ECompletionMatch *match);
- void (*restart_completion) (ECompletion *comp);
+ void (*lost_completion) (ECompletion *comp, ECompletionMatch *match);
+
void (*cancel_completion) (ECompletion *comp);
void (*end_completion) (ECompletion *comp);
void (*clear_completion) (ECompletion *comp);
- void (*lost_completion) (ECompletion *comp, ECompletionMatch *match);
};
GtkType e_completion_get_type (void);
-void e_completion_begin_search (ECompletion *comp, const gchar *text, gint pos, gint limit);
-void e_completion_cancel_search (ECompletion *comp);
+void e_completion_begin_search (ECompletion *comp, const gchar *text, gint pos, gint limit);
+void e_completion_cancel_search (ECompletion *comp);
gboolean e_completion_searching (ECompletion *comp);
+gboolean e_completion_refining (ECompletion *comp);
const gchar *e_completion_search_text (ECompletion *comp);
gint e_completion_search_text_pos (ECompletion *comp);
gint e_completion_match_count (ECompletion *comp);