gem-graph-client/libide/sourceview/ide-cursor.c

875 lines
28 KiB
C
Raw Normal View History

/* ide-cursor.c
*
* Copyright 2017 Anoop Chandu <anoopchandu96@gmail.com>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "ide-cursor"
#include "config.h"
#include <dazzle.h>
#include "ide-source-view.h"
#include "ide-cursor.h"
#include "ide-text-util.h"
struct _IdeCursor
{
GObject parent_instance;
IdeSourceView *source_view;
GtkSourceSearchContext *search_context;
GList *cursors;
GtkTextTag *highlight_tag;
DzlSignalGroup *operations_signals;
guint overwrite : 1;
};
typedef struct
{
GtkTextMark *selection_bound;
GtkTextMark *insert;
} VirtualCursor;
G_DEFINE_FINAL_TYPE (IdeCursor, ide_cursor, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_IDE_SOURCE_VIEW,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void ide_cursor_set_visible (IdeCursor *self,
GtkTextBuffer *buffer,
gboolean visible);
static void ide_cursor_set_real_cursor (IdeCursor *self,
GtkTextBuffer *buffer,
VirtualCursor *vc);
static void ide_cursor_set_virtual_cursor (IdeCursor *self,
GtkTextBuffer *buffer,
VirtualCursor *vc);
static void
ide_cursor_dispose (GObject *object)
{
IdeCursor *self = (IdeCursor *)object;
GtkTextBuffer *buffer = NULL;
g_return_if_fail (IDE_IS_CURSOR (self));
if (self->source_view != NULL)
{
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
if (self->highlight_tag != NULL)
gtk_text_tag_table_remove (gtk_text_buffer_get_tag_table (buffer),
self->highlight_tag);
g_clear_weak_pointer (&self->source_view);
}
if (self->operations_signals != NULL)
{
dzl_signal_group_set_target (self->operations_signals, NULL);
g_clear_object (&self->operations_signals);
}
g_clear_object (&self->highlight_tag);
g_clear_object (&self->search_context);
if (buffer != NULL && self->cursors != NULL)
{
for (const GList *iter = self->cursors; iter != NULL; iter = iter->next)
{
VirtualCursor *vc;
vc = iter->data;
if (buffer != NULL)
{
gtk_text_buffer_delete_mark (buffer, vc->insert);
gtk_text_buffer_delete_mark (buffer, vc->selection_bound);
}
g_slice_free (VirtualCursor, vc);
}
}
g_clear_pointer (&self->cursors, g_list_free);
G_OBJECT_CLASS (ide_cursor_parent_class)->dispose (object);
}
/* toggles the visibility of cursors */
static void
ide_cursor_set_visible (IdeCursor *self,
GtkTextBuffer *buffer,
gboolean visible)
{
g_assert (IDE_IS_CURSOR (self));
g_assert (GTK_IS_TEXT_BUFFER (buffer));
if (self->cursors != NULL)
{
for (const GList *iter = self->cursors; iter != NULL; iter = iter->next)
{
VirtualCursor *vc;
GtkTextIter selection_bound, insert;
vc = iter->data;
gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound, vc->selection_bound);
gtk_text_buffer_get_iter_at_mark (buffer, &insert, vc->insert);
if (gtk_text_iter_equal (&insert, &selection_bound))
{
if (self->overwrite)
{
gtk_text_iter_forward_char (&selection_bound);
}
else
{
gtk_text_mark_set_visible (vc->insert, visible);
continue;
}
}
if (visible)
gtk_text_buffer_apply_tag (buffer, self->highlight_tag, &selection_bound, &insert);
else
gtk_text_buffer_remove_tag (buffer, self->highlight_tag, &selection_bound, &insert);
}
}
}
/* sets real cursor at virtual cursor position */
static void
ide_cursor_set_real_cursor (IdeCursor *self,
GtkTextBuffer *buffer,
VirtualCursor *vc)
{
GtkTextIter selection_bound, insert;
g_assert (IDE_IS_CURSOR (self));
g_assert (GTK_IS_TEXT_BUFFER (buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound, vc->selection_bound);
gtk_text_buffer_get_iter_at_mark (buffer, &insert, vc->insert);
gtk_text_buffer_select_range (buffer, &insert, &selection_bound);
}
/* sets virutal cursor at actual cursor position */
static void
ide_cursor_set_virtual_cursor (IdeCursor *self,
GtkTextBuffer *buffer,
VirtualCursor *vc)
{
GtkTextIter selection_bound, insert;
g_assert (IDE_IS_CURSOR (self));
g_assert (GTK_IS_TEXT_BUFFER (buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert (buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound, gtk_text_buffer_get_selection_bound (buffer));
gtk_text_buffer_move_mark (buffer, vc->selection_bound, &selection_bound);
gtk_text_buffer_move_mark (buffer, vc->insert, &insert);
}
void
ide_cursor_remove_cursors (IdeCursor *self)
{
g_return_if_fail (IDE_IS_CURSOR (self));
if (self->cursors != NULL)
{
GtkTextBuffer *buffer;
GtkTextIter begin, end;
VirtualCursor *vc;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
ide_cursor_set_visible (self, buffer, FALSE);
for (const GList *iter = self->cursors; iter != NULL; iter = iter->next)
{
vc = iter->data;
gtk_text_buffer_delete_mark (buffer, vc->insert);
gtk_text_buffer_delete_mark (buffer, vc->selection_bound);
g_slice_free (VirtualCursor, vc);
}
g_clear_pointer (&self->cursors, g_list_free);
gtk_text_buffer_get_bounds (buffer, &begin, &end);
gtk_text_buffer_remove_tag (buffer, self->highlight_tag, &begin, &end);
}
}
static void
ide_cursor_add_cursor_by_column (IdeCursor *self)
{
GtkTextBuffer *buffer;
GtkTextIter begin, end, temp;
gint begin_line, begin_offset, end_line, end_offset, offset;
GtkTextIter iter;
g_assert (IDE_IS_CURSOR (self));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
if (!gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
return;
gtk_text_buffer_get_iter_at_mark (buffer, &temp, gtk_text_buffer_get_insert (buffer));
offset = gtk_text_iter_get_line_offset (&temp);
begin_line = gtk_text_iter_get_line (&begin);
begin_offset = gtk_text_iter_get_line_offset (&begin);
end_line = gtk_text_iter_get_line (&end);
end_offset = gtk_text_iter_get_line_offset (&end);
if (begin_line == end_line)
return;
for (int i = begin_line; i <= end_line; i++)
{
VirtualCursor *vc;
if ((i == begin_line && offset < begin_offset) ||
(i == end_line && offset > end_offset))
continue;
gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, i, offset);
vc = g_slice_new0 (VirtualCursor);
vc->selection_bound = gtk_text_buffer_create_mark (buffer, NULL, &iter, FALSE);
vc->insert = gtk_text_buffer_create_mark (buffer, NULL, &iter, FALSE);
self->cursors = g_list_prepend (self->cursors, vc);
if (self->overwrite)
{
GtkTextIter iter1 = iter;
gtk_text_iter_forward_char (&iter1);
gtk_text_buffer_apply_tag (buffer, self->highlight_tag, &iter, &iter1);
}
else
{
gtk_text_mark_set_visible (vc->insert, TRUE);
}
}
gtk_text_buffer_select_range (buffer, &iter, &iter);
}
static void
ide_cursor_add_cursor_by_position (IdeCursor *self)
{
GtkTextIter insert_iter, selection_bound_iter;
VirtualCursor *vc;
GtkTextBuffer *buffer;
g_assert (IDE_IS_CURSOR (self));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, gtk_text_buffer_get_insert (buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound_iter, gtk_text_buffer_get_selection_bound (buffer));
vc = g_slice_new0 (VirtualCursor);
vc->selection_bound = gtk_text_buffer_create_mark (buffer, NULL, &insert_iter, FALSE);
vc->insert = gtk_text_buffer_create_mark (buffer, NULL, &selection_bound_iter, FALSE);
self->cursors = g_list_prepend (self->cursors, vc);
if (gtk_text_iter_equal (&insert_iter, &selection_bound_iter))
{
if (self->overwrite)
{
gtk_text_iter_forward_char (&selection_bound_iter);
gtk_text_buffer_apply_tag (buffer, self->highlight_tag, &insert_iter, &selection_bound_iter);
}
else
{
gtk_text_mark_set_visible (vc->insert, TRUE);
}
}
else
{
gtk_text_buffer_apply_tag (buffer, self->highlight_tag, &insert_iter, &selection_bound_iter);
}
}
static void
ide_cursor_add_cursor_by_match (IdeCursor *self)
{
g_autofree gchar *text = NULL;
GtkTextIter begin, end, match_begin, match_end;
gboolean has_wrapped_around = FALSE;
VirtualCursor *vc;
GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings;
GtkTextBuffer *buffer;
g_assert (IDE_IS_CURSOR (self));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
if (!gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
return;
gtk_text_iter_order (&begin, &end);
text = gtk_text_buffer_get_text (buffer, &begin, &end, FALSE);
search_context = self->search_context;
search_settings = gtk_source_search_context_get_settings (search_context);
if (g_strcmp0 (gtk_source_search_settings_get_search_text (search_settings), text) != 0)
gtk_source_search_settings_set_search_text (search_settings, text);
if (!gtk_source_search_context_forward (search_context, &end,
&match_begin, &match_end, &has_wrapped_around))
return;
if (self->cursors == NULL)
{
vc = g_slice_new0 (VirtualCursor);
vc->selection_bound = gtk_text_buffer_create_mark (buffer, NULL, &begin, FALSE);
vc->insert = gtk_text_buffer_create_mark (buffer, NULL, &end, FALSE);
self->cursors = g_list_prepend (self->cursors, vc);
gtk_text_buffer_apply_tag (buffer, self->highlight_tag, &begin, &end);
}
vc = g_slice_new0 (VirtualCursor);
vc->selection_bound = gtk_text_buffer_create_mark (buffer, NULL, &match_begin, FALSE);
vc->insert = gtk_text_buffer_create_mark (buffer, NULL, &match_end, FALSE);
self->cursors = g_list_prepend (self->cursors, vc);
gtk_text_buffer_apply_tag (buffer, self->highlight_tag, &match_begin, &match_end);
gtk_text_buffer_select_range (buffer, &match_begin, &match_end);
ide_source_view_scroll_mark_onscreen (self->source_view, vc->insert, TRUE, 0.5, 0.5);
}
void
ide_cursor_add_cursor (IdeCursor *self,
IdeCursorType type)
{
g_return_if_fail (IDE_IS_CURSOR (self));
g_return_if_fail (type<=IDE_CURSOR_MATCH);
if (type == IDE_CURSOR_COLUMN)
ide_cursor_add_cursor_by_column (self);
else if (type == IDE_CURSOR_SELECT)
ide_cursor_add_cursor_by_position (self);
else if (type == IDE_CURSOR_MATCH)
ide_cursor_add_cursor_by_match (self);
}
void
ide_cursor_insert_text (IdeCursor *self,
gchar *text,
gint len)
{
g_return_if_fail (IDE_IS_CURSOR (self));
if (self->cursors != NULL)
{
GtkTextBuffer *buffer;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
VirtualCursor *vc = iter->data;
GtkTextIter begin, end;
gtk_text_buffer_get_iter_at_mark (buffer, &begin, vc->insert);
gtk_text_buffer_get_iter_at_mark (buffer, &end, vc->selection_bound);
if (gtk_text_iter_equal (&begin, &end))
{
if (self->overwrite)
{
gtk_text_iter_forward_char (&end);
gtk_text_buffer_delete (buffer, &begin, &end);
gtk_text_buffer_get_iter_at_mark (buffer, &end, vc->insert);
}
gtk_text_buffer_insert (buffer, &end, text, len);
}
else
{
gtk_text_buffer_delete (buffer, &begin, &end);
gtk_text_buffer_get_iter_at_mark (buffer, &end, vc->insert);
gtk_text_buffer_insert (buffer, &end, text, len);
}
}
ide_cursor_set_visible (self, buffer, TRUE);
}
}
static void
ide_cursor_backspace (GtkTextView *text_view,
IdeCursor *self)
{
g_assert (GTK_IS_TEXT_VIEW (text_view));
g_assert (IDE_IS_CURSOR (self));
if (self->cursors != NULL)
{
GtkTextBuffer *buffer;
buffer = gtk_text_view_get_buffer (text_view);
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
gtk_text_buffer_begin_user_action (buffer);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
VirtualCursor *vc = iter->data;
GtkTextIter begin, end;
gtk_text_buffer_get_iter_at_mark (buffer, &begin, vc->selection_bound);
gtk_text_buffer_get_iter_at_mark (buffer, &end, vc->insert);
if (gtk_text_iter_equal (&begin, &end))
gtk_text_buffer_backspace (buffer, &end, TRUE, gtk_text_view_get_editable (text_view));
else
gtk_text_buffer_delete (buffer, &begin, &end);
}
gtk_text_buffer_end_user_action (buffer);
ide_cursor_set_visible (self, buffer, TRUE);
}
}
static void
ide_cursor_delete_from_cursor (GtkTextView *text_view,
GtkDeleteType delete_type,
gint count,
IdeCursor *self)
{
g_assert (IDE_IS_SOURCE_VIEW (text_view));
g_assert (IDE_IS_CURSOR (self));
if (self->cursors != NULL)
{
GtkTextBuffer *buffer;
GtkTextIter ins;
GtkTextMark *mark;
buffer = gtk_text_view_get_buffer (text_view);
gtk_text_buffer_get_iter_at_mark (buffer, &ins, gtk_text_buffer_get_insert(buffer));
mark = gtk_text_buffer_create_mark (buffer, NULL, &ins, FALSE);
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
gtk_text_buffer_begin_user_action (buffer);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
VirtualCursor *vc = iter->data;
GtkTextIter begin, end;
gtk_text_buffer_get_iter_at_mark (buffer, &begin, vc->selection_bound);
gtk_text_buffer_get_iter_at_mark (buffer, &end, vc->insert);
ide_cursor_set_real_cursor (self, buffer, vc);
if (delete_type == GTK_DELETE_PARAGRAPHS)
ide_text_util_delete_line (text_view, count);
else
GTK_TEXT_VIEW_GET_CLASS (text_view)->delete_from_cursor (text_view,
delete_type,
count);
ide_cursor_set_virtual_cursor (self, buffer, vc);
}
gtk_text_buffer_end_user_action (buffer);
ide_cursor_set_visible (self, buffer, TRUE);
gtk_text_buffer_get_iter_at_mark (buffer, &ins, mark);
gtk_text_buffer_select_range (buffer, &ins, &ins);
}
}
static void
ide_cursor_delete_selection (IdeSourceView *source_view,
IdeCursor *self)
{
GtkTextBuffer *buffer;
gboolean editable;
g_assert (IDE_IS_SOURCE_VIEW (source_view));
g_assert (IDE_IS_CURSOR (self));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (source_view));
if (!editable)
return;
if (self->cursors != NULL)
{
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
gtk_text_buffer_begin_user_action (buffer);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
VirtualCursor *vc = iter->data;
GtkTextIter begin, end;
gtk_text_buffer_get_iter_at_mark (buffer, &begin, vc->selection_bound);
gtk_text_buffer_get_iter_at_mark (buffer, &end, vc->insert);
gtk_text_iter_order (&begin, &end);
if (gtk_text_iter_is_end (&end) && gtk_text_iter_starts_line (&begin))
gtk_text_iter_backward_char (&begin);
gtk_text_buffer_delete (buffer, &begin, &end);
}
gtk_text_buffer_end_user_action (buffer);
ide_cursor_set_visible (self, buffer, TRUE);
}
}
static void
ide_cursor_move_cursor (GtkTextView *text_view,
GtkMovementStep step,
gint count,
gboolean extend_selection,
IdeCursor *self)
{
g_assert (GTK_IS_TEXT_VIEW (text_view));
g_assert (IDE_IS_CURSOR (self));
if (self->cursors != NULL)
{
GtkTextBuffer *buffer;
GtkTextIter sel, ins;
buffer = gtk_text_view_get_buffer (text_view);
gtk_text_buffer_get_iter_at_mark (buffer, &ins, gtk_text_buffer_get_insert (buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &sel, gtk_text_buffer_get_selection_bound (buffer));
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
ide_cursor_set_real_cursor (self, buffer, iter->data);
GTK_TEXT_VIEW_GET_CLASS (text_view)->move_cursor (text_view,
step,
count,
extend_selection);
ide_cursor_set_virtual_cursor (self, buffer, iter->data);
}
ide_cursor_set_visible (self, buffer, TRUE);
gtk_text_buffer_select_range (buffer, &ins, &sel);
gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (buffer));
}
}
static void
ide_cursor_movement (IdeSourceView *source_view,
IdeSourceViewMovement movement,
gboolean extend_selection,
gboolean exclusive,
gboolean apply_count,
IdeCursor *self)
{
g_assert (IDE_IS_SOURCE_VIEW (source_view));
g_assert (IDE_IS_CURSOR (self));
if (self->cursors)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
GtkTextIter sel,ins;
gtk_text_buffer_get_iter_at_mark (buffer, &sel, gtk_text_buffer_get_selection_bound(buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &ins, gtk_text_buffer_get_insert(buffer));
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
ide_cursor_set_real_cursor (self, buffer, iter->data);
IDE_SOURCE_VIEW_GET_CLASS (source_view)->movement (source_view,
movement,
extend_selection,
exclusive,
apply_count);
ide_cursor_set_virtual_cursor (self, buffer, iter->data);
}
ide_cursor_set_visible (self, buffer, TRUE);
gtk_text_buffer_select_range (buffer, &ins, &sel);
}
}
static void
ide_cursor_select_inner (IdeSourceView *source_view,
const gchar *inner_left,
const gchar *inner_right,
gboolean exclusive,
gboolean string_mode,
IdeCursor *self)
{
g_assert (IDE_IS_SOURCE_VIEW (source_view));
g_assert (IDE_IS_CURSOR (self));
if (self->cursors != NULL)
{
GtkTextBuffer *buffer;
GtkTextIter sel,ins;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
gtk_text_buffer_get_iter_at_mark (buffer, &sel, gtk_text_buffer_get_selection_bound (buffer));
gtk_text_buffer_get_iter_at_mark (buffer, &ins, gtk_text_buffer_get_insert (buffer));
ide_cursor_set_visible (self, buffer, FALSE);
ide_cursor_set_virtual_cursor (self, buffer, self->cursors->data);
for (const GList *iter = self->cursors->next; iter != NULL; iter = iter->next)
{
ide_cursor_set_real_cursor (self, buffer, iter->data);
IDE_SOURCE_VIEW_GET_CLASS (source_view)->select_inner (source_view,
inner_left,
inner_right,
exclusive,
string_mode);
ide_cursor_set_virtual_cursor (self, buffer, iter->data);
}
ide_cursor_set_visible (self, buffer, TRUE);
gtk_text_buffer_select_range (buffer, &ins, &sel);
}
}
static void
ide_cursor_toggle_overwrite (GtkTextView *text_view,
IdeCursor *self)
{
GtkTextBuffer *buffer;
g_assert (GTK_IS_TEXT_VIEW (text_view));
g_assert (IDE_IS_CURSOR (self));
buffer = gtk_text_view_get_buffer (text_view);
ide_cursor_set_visible (self, buffer, FALSE);
self->overwrite = gtk_text_view_get_overwrite (text_view);
ide_cursor_set_visible (self, buffer, TRUE);
}
static void
ide_cursor_constructed (GObject *object)
{
IdeCursor *self = (IdeCursor *)object;
GtkTextView *text_view;
GtkTextBuffer *buffer;
g_autoptr(GtkSourceSearchSettings) search_settings = NULL;
G_OBJECT_CLASS (ide_cursor_parent_class)->constructed (object);
text_view = GTK_TEXT_VIEW (self->source_view);
buffer = gtk_text_view_get_buffer (text_view);
search_settings = g_object_new (GTK_SOURCE_TYPE_SEARCH_SETTINGS,
"wrap-around", FALSE,
"regex-enabled", FALSE,
"case-sensitive", TRUE,
NULL);
self->search_context = g_object_new (GTK_SOURCE_TYPE_SEARCH_CONTEXT,
"buffer", buffer,
"highlight", FALSE,
"settings", search_settings,
NULL);
gtk_text_tag_table_add (gtk_text_buffer_get_tag_table (buffer),
self->highlight_tag);
self->overwrite = gtk_text_view_get_overwrite (text_view);
dzl_signal_group_set_target (self->operations_signals, self->source_view);
}
static void
ide_cursor_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IdeCursor *self = IDE_CURSOR (object);
switch (prop_id)
{
case PROP_IDE_SOURCE_VIEW:
g_value_set_object (value, self->source_view);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_cursor_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IdeCursor *self = IDE_CURSOR (object);
switch (prop_id)
{
case PROP_IDE_SOURCE_VIEW:
g_set_weak_pointer (&self->source_view, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_cursor_class_init (IdeCursorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = ide_cursor_constructed;
object_class->dispose = ide_cursor_dispose;
object_class->get_property = ide_cursor_get_property;
object_class->set_property = ide_cursor_set_property;
properties [PROP_IDE_SOURCE_VIEW] =
g_param_spec_object ("ide-source-view",
"IdeSourceView",
"The IdeSourceView on which cursors are there",
IDE_TYPE_SOURCE_VIEW,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
ide_cursor_init (IdeCursor *self)
{
self->highlight_tag = g_object_new (GTK_TYPE_TEXT_TAG,
"underline", PANGO_UNDERLINE_SINGLE,
NULL);
self->operations_signals = dzl_signal_group_new (IDE_TYPE_SOURCE_VIEW);
dzl_signal_group_connect_object (self->operations_signals,
"move-cursor",
G_CALLBACK (ide_cursor_move_cursor),
self,
G_CONNECT_AFTER);
dzl_signal_group_connect_object (self->operations_signals,
"delete-from-cursor",
G_CALLBACK (ide_cursor_delete_from_cursor),
self,
G_CONNECT_AFTER);
dzl_signal_group_connect_object (self->operations_signals,
"backspace",
G_CALLBACK (ide_cursor_backspace),
self,
G_CONNECT_AFTER);
dzl_signal_group_connect_object (self->operations_signals,
"toggle-overwrite",
G_CALLBACK (ide_cursor_toggle_overwrite),
self,
G_CONNECT_AFTER);
dzl_signal_group_connect_object (self->operations_signals,
"movement",
G_CALLBACK (ide_cursor_movement),
self,
G_CONNECT_AFTER);
dzl_signal_group_connect_object (self->operations_signals,
"select-inner",
G_CALLBACK (ide_cursor_select_inner),
self,
G_CONNECT_AFTER);
dzl_signal_group_connect_object (self->operations_signals,
"delete-selection",
G_CALLBACK (ide_cursor_delete_selection),
self,
G_CONNECT_AFTER);
}
gboolean
ide_cursor_is_enabled (IdeCursor *self)
{
g_return_val_if_fail (IDE_IS_CURSOR (self), FALSE);
return (self->cursors != NULL);
}
void
ide_cursor_clear_highlight (IdeCursor *self)
{
GtkTextBuffer *buffer;
GtkTextIter begin, end;
g_return_if_fail (IDE_IS_CURSOR (self));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->source_view));
gtk_text_buffer_get_bounds (buffer, &begin, &end);
gtk_text_buffer_remove_tag (buffer, self->highlight_tag, &begin, &end);
}