diff options
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/text/e-completion-match.c | 11 | ||||
-rw-r--r-- | widgets/text/e-completion-match.h | 1 | ||||
-rw-r--r-- | widgets/text/e-completion-test.c | 6 | ||||
-rw-r--r-- | widgets/text/e-completion.c | 211 | ||||
-rw-r--r-- | widgets/text/e-completion.h | 23 |
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); |