Blame gtksourceview/gtksourcecompletioncontext.c

Packit a7d494
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- *
Packit a7d494
 * gtksourcecompletioncontext.c
Packit a7d494
 * This file is part of GtkSourceView
Packit a7d494
 *
Packit a7d494
 * Copyright (C) 2009 - Jesse van den Kieboom <jessevdk@gnome.org>
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is free software; you can redistribute it and/or
Packit a7d494
 * modify it under the terms of the GNU Lesser General Public
Packit a7d494
 * License as published by the Free Software Foundation; either
Packit a7d494
 * version 2.1 of the License, or (at your option) any later version.
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is distributed in the hope that it will be useful,
Packit a7d494
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a7d494
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a7d494
 * Lesser General Public License for more details.
Packit a7d494
 *
Packit a7d494
 * You should have received a copy of the GNU Lesser General Public
Packit a7d494
 * License along with this library; if not, write to the Free Software
Packit a7d494
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit a7d494
 */
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * SECTION:completioncontext
Packit a7d494
 * @title: GtkSourceCompletionContext
Packit a7d494
 * @short_description: The context of a completion
Packit a7d494
 *
Packit a7d494
 * Initially, the completion window is hidden. For a completion to occur, it has
Packit a7d494
 * to be activated. The different possible activations are listed in
Packit a7d494
 * #GtkSourceCompletionActivation. When an activation occurs, a
Packit a7d494
 * #GtkSourceCompletionContext object is created, and the eligible providers are
Packit a7d494
 * asked to add proposals with gtk_source_completion_context_add_proposals().
Packit a7d494
 *
Packit a7d494
 * If no proposals are added, the completion window remains hidden, and the
Packit a7d494
 * context is destroyed.
Packit a7d494
 *
Packit a7d494
 * On the other hand, if proposals are added, the completion window becomes
Packit a7d494
 * visible, and the user can choose a proposal. If the user is not happy with
Packit a7d494
 * the shown proposals, he or she can insert or delete characters, to modify the
Packit a7d494
 * completion context and therefore hoping to see the proposal he or she wants.
Packit a7d494
 * This means that when an insertion or deletion occurs in the #GtkTextBuffer
Packit a7d494
 * when the completion window is visible, the eligible providers are again asked
Packit a7d494
 * to add proposals. The #GtkSourceCompletionContext:activation remains the
Packit a7d494
 * same in this case.
Packit a7d494
 *
Packit a7d494
 * When the completion window is hidden, the interactive completion is triggered
Packit a7d494
 * only on insertion in the buffer, not on deletion. Once the completion window
Packit a7d494
 * is visible, then on each insertion or deletion, there is a new population and
Packit a7d494
 * the providers are asked to add proposals. If there are no more proposals, the
Packit a7d494
 * completion window disappears. So if you want to keep the completion window
Packit a7d494
 * visible, but there are no proposals, you can insert a dummy proposal named
Packit a7d494
 * "No proposals". For example, the user types progressively the name of
Packit a7d494
 * a function, and some proposals appear. The user types a bad character and
Packit a7d494
 * there are no proposals anymore. What the user wants is to delete the last
Packit a7d494
 * character, and see the previous proposals. If the completion window
Packit a7d494
 * disappears, the previous proposals will not reappear on the character
Packit a7d494
 * deletion.
Packit a7d494
 *
Packit a7d494
 * A #GtkTextIter is associated with the context, this is where the completion
Packit a7d494
 * takes place. With this #GtkTextIter, you can get the associated
Packit a7d494
 * #GtkTextBuffer with gtk_text_iter_get_buffer().
Packit a7d494
 */
Packit a7d494
Packit a7d494
#ifdef HAVE_CONFIG_H
Packit a7d494
#include <config.h>
Packit a7d494
#endif
Packit a7d494
Packit a7d494
#include "gtksourcecompletioncontext.h"
Packit a7d494
#include "gtksourceview-enumtypes.h"
Packit a7d494
#include "gtksourcecompletionprovider.h"
Packit a7d494
#include "gtksourceview-i18n.h"
Packit a7d494
#include "gtksourcecompletion.h"
Packit a7d494
Packit a7d494
struct _GtkSourceCompletionContextPrivate
Packit a7d494
{
Packit a7d494
	GtkSourceCompletion *completion;
Packit a7d494
Packit a7d494
	GtkTextMark *mark;
Packit a7d494
	GtkSourceCompletionActivation activation;
Packit a7d494
};
Packit a7d494
Packit a7d494
enum
Packit a7d494
{
Packit a7d494
	PROP_0,
Packit a7d494
	PROP_COMPLETION,
Packit a7d494
	PROP_ITER,
Packit a7d494
	PROP_ACTIVATION
Packit a7d494
};
Packit a7d494
Packit a7d494
enum
Packit a7d494
{
Packit a7d494
	CANCELLED,
Packit a7d494
	N_SIGNALS
Packit a7d494
};
Packit a7d494
Packit a7d494
static guint context_signals[N_SIGNALS];
Packit a7d494
Packit a7d494
G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceCompletionContext, gtk_source_completion_context, G_TYPE_INITIALLY_UNOWNED)
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_completion_context_dispose (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
Packit a7d494
Packit a7d494
	if (context->priv->mark != NULL)
Packit a7d494
	{
Packit a7d494
		GtkTextBuffer *buffer = gtk_text_mark_get_buffer (context->priv->mark);
Packit a7d494
Packit a7d494
		if (buffer != NULL)
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_delete_mark (buffer, context->priv->mark);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		g_object_unref (context->priv->mark);
Packit a7d494
		context->priv->mark = NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	g_clear_object (&context->priv->completion);
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_completion_context_parent_class)->dispose (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
set_iter (GtkSourceCompletionContext *context,
Packit a7d494
	  GtkTextIter                *iter)
Packit a7d494
{
Packit a7d494
	GtkTextBuffer *buffer;
Packit a7d494
Packit a7d494
	buffer = gtk_text_iter_get_buffer (iter);
Packit a7d494
Packit a7d494
	if (context->priv->mark != NULL)
Packit a7d494
	{
Packit a7d494
		GtkTextBuffer *old_buffer;
Packit a7d494
Packit a7d494
		old_buffer = gtk_text_mark_get_buffer (context->priv->mark);
Packit a7d494
Packit a7d494
		if (old_buffer != buffer)
Packit a7d494
		{
Packit a7d494
			g_object_unref (context->priv->mark);
Packit a7d494
			context->priv->mark = NULL;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (context->priv->mark == NULL)
Packit a7d494
	{
Packit a7d494
		context->priv->mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
Packit a7d494
		g_object_ref (context->priv->mark);
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		gtk_text_buffer_move_mark (buffer, context->priv->mark, iter);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	g_object_notify (G_OBJECT (context), "iter");
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_completion_context_set_property (GObject      *object,
Packit a7d494
                                            guint         prop_id,
Packit a7d494
                                            const GValue *value,
Packit a7d494
                                            GParamSpec   *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_COMPLETION:
Packit a7d494
			context->priv->completion = g_value_dup_object (value);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_ITER:
Packit a7d494
			set_iter (context, g_value_get_boxed (value));
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_ACTIVATION:
Packit a7d494
			context->priv->activation = g_value_get_flags (value);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_completion_context_get_property (GObject    *object,
Packit a7d494
                                            guint       prop_id,
Packit a7d494
                                            GValue     *value,
Packit a7d494
                                            GParamSpec *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_COMPLETION:
Packit a7d494
			g_value_set_object (value, context->priv->completion);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_ITER:
Packit a7d494
			{
Packit a7d494
				GtkTextIter iter;
Packit a7d494
Packit a7d494
				if (gtk_source_completion_context_get_iter (context, &iter))
Packit a7d494
				{
Packit a7d494
					g_value_set_boxed (value, &iter);
Packit a7d494
				}
Packit a7d494
			}
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_ACTIVATION:
Packit a7d494
			g_value_set_flags (value, context->priv->activation);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_completion_context_class_init (GtkSourceCompletionContextClass *klass)
Packit a7d494
{
Packit a7d494
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit a7d494
Packit a7d494
	object_class->set_property = gtk_source_completion_context_set_property;
Packit a7d494
	object_class->get_property = gtk_source_completion_context_get_property;
Packit a7d494
	object_class->dispose = gtk_source_completion_context_dispose;
Packit a7d494
Packit a7d494
	/**
Packit a7d494
	 * GtkSourceCompletionContext::cancelled:
Packit a7d494
	 *
Packit a7d494
	 * Emitted when the current population of proposals has been cancelled.
Packit a7d494
	 * Providers adding proposals asynchronously should connect to this signal
Packit a7d494
	 * to know when to cancel running proposal queries.
Packit a7d494
	 **/
Packit a7d494
	context_signals[CANCELLED] =
Packit a7d494
		g_signal_new ("cancelled",
Packit a7d494
		              G_TYPE_FROM_CLASS (klass),
Packit a7d494
		              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
Packit a7d494
		              G_STRUCT_OFFSET (GtkSourceCompletionContextClass, cancelled),
Packit a7d494
		              NULL, NULL, NULL,
Packit a7d494
		              G_TYPE_NONE, 0);
Packit a7d494
Packit a7d494
	/**
Packit a7d494
	 * GtkSourceCompletionContext:completion:
Packit a7d494
	 *
Packit a7d494
	 * The #GtkSourceCompletion associated with the context.
Packit a7d494
	 **/
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
	                                 PROP_COMPLETION,
Packit a7d494
	                                 g_param_spec_object ("completion",
Packit a7d494
	                                                      "Completion",
Packit a7d494
	                                                      "The completion object to which the context belongs",
Packit a7d494
	                                                      GTK_SOURCE_TYPE_COMPLETION,
Packit a7d494
	                                                      G_PARAM_READWRITE |
Packit a7d494
							      G_PARAM_CONSTRUCT_ONLY |
Packit a7d494
							      G_PARAM_STATIC_STRINGS));
Packit a7d494
Packit a7d494
	/**
Packit a7d494
	 * GtkSourceCompletionContext:iter:
Packit a7d494
	 *
Packit a7d494
	 * The #GtkTextIter at which the completion is invoked.
Packit a7d494
	 **/
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
	                                 PROP_ITER,
Packit a7d494
					 g_param_spec_boxed ("iter",
Packit a7d494
							     "Iterator",
Packit a7d494
							     "The GtkTextIter at which the completion was invoked",
Packit a7d494
							     GTK_TYPE_TEXT_ITER,
Packit a7d494
							     G_PARAM_READWRITE |
Packit a7d494
							     G_PARAM_STATIC_STRINGS));
Packit a7d494
Packit a7d494
	/**
Packit a7d494
	 * GtkSourceCompletionContext:activation:
Packit a7d494
	 *
Packit a7d494
	 * The completion activation
Packit a7d494
	 **/
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
	                                 PROP_ACTIVATION,
Packit a7d494
	                                 g_param_spec_flags ("activation",
Packit a7d494
	                                                     "Activation",
Packit a7d494
	                                                     "The type of activation",
Packit a7d494
	                                                     GTK_SOURCE_TYPE_COMPLETION_ACTIVATION,
Packit a7d494
	                                                     GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED,
Packit a7d494
	                                                     G_PARAM_READWRITE |
Packit a7d494
							     G_PARAM_CONSTRUCT |
Packit a7d494
							     G_PARAM_STATIC_STRINGS));
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_completion_context_init (GtkSourceCompletionContext *context)
Packit a7d494
{
Packit a7d494
	context->priv = gtk_source_completion_context_get_instance_private (context);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_completion_context_add_proposals:
Packit a7d494
 * @context: a #GtkSourceCompletionContext.
Packit a7d494
 * @provider: a #GtkSourceCompletionProvider.
Packit a7d494
 * @proposals: (nullable) (element-type GtkSource.CompletionProposal): The list of proposals to add.
Packit a7d494
 * @finished: Whether the provider is finished adding proposals.
Packit a7d494
 *
Packit a7d494
 * Providers can use this function to add proposals to the completion. They
Packit a7d494
 * can do so asynchronously by means of the @finished argument. Providers must
Packit a7d494
 * ensure that they always call this function with @finished set to %TRUE
Packit a7d494
 * once each population (even if no proposals need to be added).
Packit a7d494
 * Population occurs when the gtk_source_completion_provider_populate()
Packit a7d494
 * function is called.
Packit a7d494
 **/
Packit a7d494
void
Packit a7d494
gtk_source_completion_context_add_proposals (GtkSourceCompletionContext  *context,
Packit a7d494
                                             GtkSourceCompletionProvider *provider,
Packit a7d494
                                             GList                       *proposals,
Packit a7d494
                                             gboolean                     finished)
Packit a7d494
{
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
Packit a7d494
Packit a7d494
	_gtk_source_completion_add_proposals (context->priv->completion,
Packit a7d494
	                                      context,
Packit a7d494
	                                      provider,
Packit a7d494
	                                      proposals,
Packit a7d494
	                                      finished);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_completion_context_get_iter:
Packit a7d494
 * @context: a #GtkSourceCompletionContext.
Packit a7d494
 * @iter: (out): a #GtkTextIter.
Packit a7d494
 *
Packit a7d494
 * Get the iter at which the completion was invoked. Providers can use this
Packit a7d494
 * to determine how and if to match proposals.
Packit a7d494
 *
Packit a7d494
 * Returns: %TRUE if @iter is correctly set, %FALSE otherwise.
Packit a7d494
 **/
Packit a7d494
gboolean
Packit a7d494
gtk_source_completion_context_get_iter (GtkSourceCompletionContext *context,
Packit a7d494
                                        GtkTextIter                *iter)
Packit a7d494
{
Packit a7d494
	GtkTextBuffer *mark_buffer;
Packit a7d494
	GtkSourceView *view;
Packit a7d494
	GtkTextBuffer *completion_buffer;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), FALSE);
Packit a7d494
Packit a7d494
	if (context->priv->mark == NULL)
Packit a7d494
	{
Packit a7d494
		/* This should never happen: context should be always be created
Packit a7d494
		   providing a position iter */
Packit a7d494
		g_warning ("Completion context without mark");
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	mark_buffer = gtk_text_mark_get_buffer (context->priv->mark);
Packit a7d494
Packit a7d494
	if (mark_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	view = gtk_source_completion_get_view (context->priv->completion);
Packit a7d494
	if (view == NULL)
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	completion_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
Packit a7d494
Packit a7d494
	if (completion_buffer != mark_buffer)
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_text_buffer_get_iter_at_mark (mark_buffer, iter, context->priv->mark);
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_completion_context_get_activation:
Packit a7d494
 * @context: a #GtkSourceCompletionContext.
Packit a7d494
 *
Packit a7d494
 * Get the context activation.
Packit a7d494
 *
Packit a7d494
 * Returns: The context activation.
Packit a7d494
 */
Packit a7d494
GtkSourceCompletionActivation
Packit a7d494
gtk_source_completion_context_get_activation (GtkSourceCompletionContext *context)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context),
Packit a7d494
			      GTK_SOURCE_COMPLETION_ACTIVATION_NONE);
Packit a7d494
Packit a7d494
	return context->priv->activation;
Packit a7d494
}
Packit a7d494
Packit a7d494
void
Packit a7d494
_gtk_source_completion_context_cancel (GtkSourceCompletionContext *context)
Packit a7d494
{
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
Packit a7d494
Packit a7d494
	g_signal_emit (context, context_signals[CANCELLED], 0);
Packit a7d494
}
Packit a7d494
Packit a7d494
GtkSourceCompletionContext *
Packit a7d494
_gtk_source_completion_context_new (GtkSourceCompletion *completion,
Packit a7d494
				    GtkTextIter         *position)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), NULL);
Packit a7d494
	g_return_val_if_fail (position != NULL, NULL);
Packit a7d494
Packit a7d494
	return g_object_new (GTK_SOURCE_TYPE_COMPLETION_CONTEXT,
Packit a7d494
	                     "completion", completion,
Packit a7d494
	                     "iter", position,
Packit a7d494
	                      NULL);
Packit a7d494
}