1620 lines
42 KiB
C
1620 lines
42 KiB
C
/*
|
|
* mooterm/mootermselection.c
|
|
*
|
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* 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.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#define MOOTERM_COMPILATION
|
|
#include "mooterm/mooterm-text-private.h"
|
|
#include "mooterm/mooterm-private.h"
|
|
#include "mooterm/mootermbuffer-private.h"
|
|
#include "mooterm/mooterm-selection.h"
|
|
#include "mooterm/mootermline-private.h"
|
|
|
|
|
|
typedef struct {
|
|
MooTermIter start;
|
|
MooTermIter end;
|
|
} Segment;
|
|
|
|
#define GET_SELECTION(term__) ((Segment*)(term__)->priv->selection)
|
|
|
|
#define ITER_TOTAL_HEIGHT(iter__) ((int)((iter__)->term->priv->height + buf_scrollback ((iter__)->buffer)))
|
|
#define ITER_TERM(iter__) ((iter__)->term)
|
|
#define ITER_ROW(iter__) ((iter__)->row)
|
|
#define ITER_COL(iter__) ((iter__)->col)
|
|
#define ITER_WIDTH(iter__) ((iter__)->width)
|
|
|
|
#define FILL_ITER(iter__,term__,row__,col__) \
|
|
(iter__)->term = term__; \
|
|
(iter__)->buffer = term__->priv->buffer; \
|
|
(iter__)->line = buf_line ((iter__)->buffer, row__); \
|
|
(iter__)->row = row__; \
|
|
(iter__)->col = col__; \
|
|
(iter__)->width = term__->priv->width; \
|
|
CHECK_ITER(iter__)
|
|
|
|
|
|
#ifdef DEBUG
|
|
static void CHECK_ITER (const MooTermIter *iter)
|
|
{
|
|
g_assert (iter->term != NULL);
|
|
g_assert ((int)iter->term->priv->width == iter->width);
|
|
g_assert (iter->row >= 0);
|
|
g_assert (iter->row < ITER_TOTAL_HEIGHT(iter));
|
|
g_assert (iter->col >= 0);
|
|
g_assert (iter->col <= iter->width);
|
|
}
|
|
|
|
static void CHECK_SEGMENT (const Segment *segment)
|
|
{
|
|
CHECK_ITER (&segment->start);
|
|
CHECK_ITER (&segment->end);
|
|
}
|
|
#else
|
|
#define CHECK_ITER(iter__)
|
|
#define CHECK_SEGMENT(segment__)
|
|
#endif
|
|
|
|
|
|
static void get_start_iter (MooTerm *term,
|
|
MooTermIter *iter);
|
|
static void get_end_iter (MooTerm *term,
|
|
MooTermIter *iter);
|
|
static int iter_cmp (const MooTermIter *first,
|
|
const MooTermIter *second);
|
|
static void iter_set_start (MooTermIter *iter);
|
|
|
|
static char *segment_get_text (Segment *segment);
|
|
static void invalidate_segment (Segment *segments,
|
|
guint num);
|
|
|
|
static void middle_button_click (MooTerm *term);
|
|
static void right_button_click (MooTerm *term,
|
|
GdkEventButton *event);
|
|
#define MOUSE_STUFF(term__) (&(term__)->priv->mouse_stuff)
|
|
|
|
|
|
gpointer
|
|
_moo_term_selection_new (MooTerm *term)
|
|
{
|
|
Segment *sel = g_new0 (Segment, 1);
|
|
|
|
FILL_ITER (&sel->start, term, 0, 0);
|
|
FILL_ITER (&sel->end, term, 0, 0);
|
|
|
|
return sel;
|
|
}
|
|
|
|
|
|
void
|
|
_moo_term_selection_invalidate (MooTerm *term)
|
|
{
|
|
Segment *sel = term->priv->selection;
|
|
|
|
invalidate_segment (sel, 1);
|
|
FILL_ITER (&sel->start, term, 0, 0);
|
|
FILL_ITER (&sel->end, term, 0, 0);
|
|
|
|
_moo_term_release_selection (term);
|
|
}
|
|
|
|
|
|
static gboolean segment_empty (Segment *s)
|
|
{
|
|
CHECK_SEGMENT (s);
|
|
return !iter_cmp (&s->start, &s->end);
|
|
}
|
|
|
|
|
|
static int segment_sym_diff (Segment *s1, Segment *s2,
|
|
Segment *result)
|
|
{
|
|
Segment *left = result;
|
|
Segment *right = &result[1];
|
|
|
|
CHECK_SEGMENT (s1);
|
|
CHECK_SEGMENT (s2);
|
|
|
|
if (segment_empty (s1))
|
|
{
|
|
if (segment_empty (s2))
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
*left = *s2;
|
|
CHECK_SEGMENT (left);
|
|
return 1;
|
|
}
|
|
}
|
|
else if (segment_empty (s2))
|
|
{
|
|
*left = *s1;
|
|
CHECK_SEGMENT (left);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
Segment tmp;
|
|
MooTermIter itmp;
|
|
|
|
*left = *s1;
|
|
*right = *s2;
|
|
_moo_term_iter_order (&left->start, &left->end);
|
|
_moo_term_iter_order (&right->start, &right->end);
|
|
|
|
switch (iter_cmp (&left->start, &right->start))
|
|
{
|
|
case 0:
|
|
switch (iter_cmp (&left->end, &right->end))
|
|
{
|
|
case 0:
|
|
return 0;
|
|
|
|
case 1:
|
|
left->start = right->end;
|
|
CHECK_SEGMENT (left);
|
|
return 1;
|
|
|
|
case -1:
|
|
left->start = left->end;
|
|
left->end = right->end;
|
|
CHECK_SEGMENT (left);
|
|
return 1;
|
|
}
|
|
|
|
case 1:
|
|
tmp = *left;
|
|
*left = *right;
|
|
*right = tmp;
|
|
break;
|
|
}
|
|
|
|
switch (iter_cmp (&left->end, &right->start))
|
|
{
|
|
case 0:
|
|
left->end = right->end;
|
|
CHECK_SEGMENT (left);
|
|
return 1;
|
|
|
|
case -1:
|
|
CHECK_SEGMENT (left);
|
|
CHECK_SEGMENT (right);
|
|
return 2;
|
|
|
|
case 1:
|
|
itmp = left->end;
|
|
left->end = right->start;
|
|
right->start = itmp;
|
|
CHECK_SEGMENT (left);
|
|
CHECK_SEGMENT (right);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
g_return_val_if_reached (0);
|
|
}
|
|
|
|
|
|
static void invalidate_segment (Segment *segm, guint num)
|
|
{
|
|
MooTerm *term;
|
|
int top_line;
|
|
guint i;
|
|
|
|
if (!num)
|
|
return;
|
|
|
|
term = ITER_TERM (&segm->start);
|
|
top_line = term_top_line (term);
|
|
|
|
for (i = 0; i < num; ++i)
|
|
{
|
|
MooTermIter start = segm[i].start;
|
|
MooTermIter end = segm[i].end;
|
|
GdkRectangle rect;
|
|
|
|
_moo_term_iter_order (&start, &end);
|
|
|
|
if (ITER_ROW (&start) < ITER_ROW (&end))
|
|
{
|
|
if (ITER_COL (&start) < (int)term->priv->width)
|
|
{
|
|
rect.x = ITER_COL (&start);
|
|
rect.width = term->priv->width - rect.x;
|
|
rect.y = ITER_ROW (&start) - top_line;
|
|
rect.height = 1;
|
|
_moo_term_invalidate_rect (term, &rect);
|
|
}
|
|
|
|
if (ITER_ROW (&start) + 1 < ITER_ROW (&end))
|
|
{
|
|
rect.x = 0;
|
|
rect.width = term->priv->width;
|
|
rect.y = ITER_ROW (&start) + 1 - top_line;
|
|
rect.height = ITER_ROW (&end) - ITER_ROW (&start) - 1;
|
|
_moo_term_invalidate_rect (term, &rect);
|
|
}
|
|
|
|
if (ITER_COL (&end) > 0)
|
|
{
|
|
rect.x = 0;
|
|
rect.width = ITER_COL (&end);
|
|
rect.y = ITER_ROW (&end) - top_line;
|
|
rect.height = 1;
|
|
_moo_term_invalidate_rect (term, &rect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ITER_COL (&start) < ITER_COL (&end))
|
|
{
|
|
rect.x = ITER_COL (&start);
|
|
rect.width = ITER_COL (&end) - ITER_COL (&start);
|
|
rect.y = ITER_ROW (&start) - top_line;
|
|
rect.height = 1;
|
|
_moo_term_invalidate_rect (term, &rect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
_moo_term_select_range (MooTerm *term,
|
|
const MooTermIter *start,
|
|
const MooTermIter *end)
|
|
{
|
|
Segment diff[2];
|
|
Segment new_sel;
|
|
Segment old_selection;
|
|
gboolean inv = FALSE;
|
|
|
|
CHECK_ITER (start);
|
|
CHECK_ITER (end);
|
|
|
|
old_selection = *GET_SELECTION (ITER_TERM (start));
|
|
CHECK_SEGMENT (&old_selection);
|
|
|
|
new_sel.start = *start;
|
|
new_sel.end = *end;
|
|
|
|
switch (iter_cmp (start, end))
|
|
{
|
|
case 0:
|
|
iter_set_start (&new_sel.start);
|
|
iter_set_start (&new_sel.end);
|
|
break;
|
|
|
|
case 1:
|
|
inv = TRUE;
|
|
break;
|
|
}
|
|
|
|
_moo_term_iter_order (&new_sel.start, &new_sel.end);
|
|
|
|
if (ITER_COL(&new_sel.start) == ITER_WIDTH(&new_sel.start))
|
|
{
|
|
g_assert (ITER_ROW(&new_sel.start) < ITER_TOTAL_HEIGHT(&new_sel.start));
|
|
if (ITER_ROW(&new_sel.start) + 1 < ITER_TOTAL_HEIGHT(&new_sel.start))
|
|
{
|
|
ITER_ROW(&new_sel.start)++;
|
|
ITER_COL(&new_sel.start) = 0;
|
|
}
|
|
}
|
|
|
|
if (ITER_COL(&new_sel.end) == 0 && ITER_ROW(&new_sel.end) != 0)
|
|
{
|
|
ITER_ROW(&new_sel.end)--;
|
|
ITER_COL(&new_sel.end) = ITER_WIDTH(&new_sel.start);
|
|
}
|
|
|
|
if (inv)
|
|
_moo_term_iter_order (&new_sel.end, &new_sel.start);
|
|
|
|
CHECK_SEGMENT (&new_sel);
|
|
|
|
*GET_SELECTION (ITER_TERM (start)) = new_sel;
|
|
|
|
invalidate_segment (diff,
|
|
segment_sym_diff (&new_sel,
|
|
&old_selection,
|
|
diff));
|
|
|
|
if (_moo_term_selection_empty (term))
|
|
_moo_term_release_selection (term);
|
|
else
|
|
_moo_term_grab_selection (term);
|
|
}
|
|
|
|
|
|
static void get_start_iter (MooTerm *term,
|
|
MooTermIter *iter)
|
|
{
|
|
FILL_ITER (iter, term, 0, 0);
|
|
}
|
|
|
|
|
|
static void get_end_iter (MooTerm *term,
|
|
MooTermIter *iter)
|
|
{
|
|
int width = term->priv->width;
|
|
int total_height = term->priv->height + buf_scrollback (term->priv->buffer);
|
|
FILL_ITER (iter, term, total_height - 1, width);
|
|
}
|
|
|
|
|
|
/**************************************************************************/
|
|
/* mouse functionality
|
|
*/
|
|
|
|
static void start_drag_scroll (MooTerm *term);
|
|
static void stop_drag_scroll (MooTerm *term);
|
|
static gboolean drag_scroll_timeout_func (MooTerm *term);
|
|
static void scroll_selection_end_onscreen (MooTerm *term);
|
|
static void start_selection_dnd (MooTerm *term,
|
|
const MooTermIter *iter,
|
|
GdkEventMotion *event);
|
|
|
|
#define SCROLL_TIMEOUT 100
|
|
|
|
|
|
gboolean
|
|
_moo_term_button_press (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
MooTermIter iter;
|
|
int x, y;
|
|
MooTerm *term = MOO_TERM (widget);
|
|
|
|
moo_term_set_pointer_visible (term, TRUE);
|
|
|
|
if (!GTK_WIDGET_HAS_FOCUS (widget))
|
|
gtk_widget_grab_focus (widget);
|
|
|
|
moo_term_window_to_buffer_coords (term, event->x, event->y, &x, &y);
|
|
moo_term_get_iter_at_location (term, &iter, x, y);
|
|
|
|
if (event->type == GDK_BUTTON_PRESS)
|
|
{
|
|
if (event->button == 1)
|
|
{
|
|
#if 0
|
|
MooTermIter sel_start, sel_end;
|
|
#endif
|
|
|
|
MOUSE_STUFF(term)->drag_button = GDK_BUTTON_PRESS;
|
|
MOUSE_STUFF(term)->drag_start_x = x;
|
|
MOUSE_STUFF(term)->drag_start_y = y;
|
|
|
|
#if 0
|
|
/* TODO: implement drag'n'drop */
|
|
/* if clicked in selected, start drag */
|
|
if (_moo_term_get_selection_bounds (term, &sel_start, &sel_end))
|
|
{
|
|
moo_term_iter_order (&sel_start, &sel_end);
|
|
if (_moo_term_iter_in_range (&iter, &sel_start, &sel_end)
|
|
&& FALSE)
|
|
{
|
|
/* clicked inside of selection,
|
|
* set up drag and return */
|
|
MOUSE_STUFF(term)->drag_type = DRAG_DRAG;
|
|
return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* otherwise, clear selection, and position cursor at clicked point */
|
|
if (event->state & GDK_SHIFT_MASK)
|
|
_moo_term_place_selection_end (term, &iter);
|
|
else
|
|
_moo_term_select_range (term, &iter, &iter);
|
|
|
|
MOUSE_STUFF(term)->drag_type = DRAG_SELECT;
|
|
}
|
|
else if (event->button == 2)
|
|
{
|
|
middle_button_click (term);
|
|
}
|
|
else if (event->button == 3)
|
|
{
|
|
right_button_click (term, event);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("got button %d in button_press callback", event->button);
|
|
}
|
|
}
|
|
else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
|
|
{
|
|
MooTermIter bound;
|
|
|
|
if (_moo_term_get_selection_bounds (term, NULL, NULL))
|
|
{
|
|
/* it may happen sometimes, if you click fast enough */
|
|
_moo_term_select_range (term, &iter, &iter);
|
|
}
|
|
|
|
MOUSE_STUFF(term)->drag_button = GDK_2BUTTON_PRESS;
|
|
MOUSE_STUFF(term)->drag_start_x = x;
|
|
MOUSE_STUFF(term)->drag_start_y = y;
|
|
MOUSE_STUFF(term)->drag_type = DRAG_SELECT;
|
|
|
|
bound = iter;
|
|
|
|
if (_moo_term_extend_selection (term, MOO_TEXT_SELECT_WORDS, &iter, &bound))
|
|
_moo_term_select_range (term, &bound, &iter);
|
|
}
|
|
else if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
|
|
{
|
|
MooTermIter bound;
|
|
|
|
MOUSE_STUFF(term)->drag_button = GDK_3BUTTON_PRESS;
|
|
MOUSE_STUFF(term)->drag_start_x = x;
|
|
MOUSE_STUFF(term)->drag_start_y = y;
|
|
MOUSE_STUFF(term)->drag_type = DRAG_SELECT;
|
|
|
|
bound = iter;
|
|
|
|
if (_moo_term_extend_selection (term, MOO_TEXT_SELECT_LINES, &iter, &bound))
|
|
_moo_term_select_range (term, &bound, &iter);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_term_button_release (GtkWidget *widget,
|
|
G_GNUC_UNUSED GdkEventButton *event)
|
|
{
|
|
MooTerm *term = MOO_TERM (widget);
|
|
MooTermIter iter;
|
|
|
|
moo_term_set_pointer_visible (term, TRUE);
|
|
|
|
switch (MOUSE_STUFF(term)->drag_type)
|
|
{
|
|
case DRAG_NONE:
|
|
/* it may happen after right-click, or clicking outside
|
|
* of widget or something like that
|
|
* everything has been taken care of, so do nothing */
|
|
break;
|
|
|
|
case DRAG_SELECT:
|
|
/* everything should be done already in button_press and
|
|
* motion_notify handlers */
|
|
stop_drag_scroll (term);
|
|
break;
|
|
|
|
case DRAG_DRAG:
|
|
/* if we were really dragging, drop it
|
|
* otherwise, it was just a single click in selected text */
|
|
g_assert (!MOUSE_STUFF(term)->drag_moved); /* parent should handle drag */ /* TODO ??? */
|
|
|
|
moo_term_get_iter_at_location (term, &iter,
|
|
MOUSE_STUFF(term)->drag_start_x,
|
|
MOUSE_STUFF(term)->drag_start_y);
|
|
_moo_term_select_range (term, &iter, &iter);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
MOUSE_STUFF(term)->drag_moved = FALSE;
|
|
MOUSE_STUFF(term)->drag_type = DRAG_NONE;
|
|
MOUSE_STUFF(term)->drag_start_x = -1;
|
|
MOUSE_STUFF(term)->drag_start_y = -1;
|
|
MOUSE_STUFF(term)->drag_button = GDK_BUTTON_RELEASE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define OUTSIDE(x,y,rect) \
|
|
((x) < (rect).x || \
|
|
(y) < (rect).y || \
|
|
(x) >= (rect).x + (rect).width || \
|
|
(y) >= (rect).y + (rect).height)
|
|
|
|
|
|
gboolean
|
|
_moo_term_motion_notify (GtkWidget *widget,
|
|
GdkEventMotion *event)
|
|
{
|
|
MooTerm *term = MOO_TERM (widget);
|
|
int x, y, event_x, event_y;
|
|
MooTermIter iter;
|
|
|
|
moo_term_set_pointer_visible (MOO_TERM (widget), TRUE);
|
|
|
|
if (!MOUSE_STUFF(term)->drag_type)
|
|
return FALSE;
|
|
|
|
if (event->is_hint)
|
|
{
|
|
gdk_window_get_pointer (event->window, &event_x, &event_y, NULL);
|
|
}
|
|
else {
|
|
event_x = (int)event->x;
|
|
event_y = (int)event->y;
|
|
}
|
|
|
|
moo_term_window_to_buffer_coords (term, event_x, event_y, &x, &y);
|
|
moo_term_get_iter_at_location (term, &iter, x, y);
|
|
|
|
if (MOUSE_STUFF(term)->drag_type == DRAG_SELECT)
|
|
{
|
|
GdkRectangle rect;
|
|
MooTermIter start;
|
|
MooTextSelectionType t;
|
|
|
|
MOUSE_STUFF(term)->drag_moved = TRUE;
|
|
_moo_term_get_visible_rect (term, &rect);
|
|
|
|
if (OUTSIDE (x, y, rect))
|
|
{
|
|
start_drag_scroll (term);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
stop_drag_scroll (term);
|
|
}
|
|
|
|
moo_term_get_iter_at_location (term, &start,
|
|
MOUSE_STUFF(term)->drag_start_x,
|
|
MOUSE_STUFF(term)->drag_start_y);
|
|
|
|
switch (MOUSE_STUFF(term)->drag_button)
|
|
{
|
|
case GDK_BUTTON_PRESS:
|
|
t = MOO_TEXT_SELECT_CHARS;
|
|
break;
|
|
case GDK_2BUTTON_PRESS:
|
|
t = MOO_TEXT_SELECT_WORDS;
|
|
break;
|
|
default:
|
|
t = MOO_TEXT_SELECT_LINES;
|
|
}
|
|
|
|
if (_moo_term_extend_selection (term, t, &iter, &start))
|
|
_moo_term_select_range (term, &start, &iter);
|
|
else
|
|
_moo_term_select_range (term, &iter, &iter);
|
|
}
|
|
else
|
|
{
|
|
/* this piece is from gtktextview.c */
|
|
int x, y;
|
|
|
|
gdk_window_get_pointer (widget->window, &x, &y, NULL);
|
|
|
|
if (gtk_drag_check_threshold (widget, MOUSE_STUFF(term)->drag_start_x,
|
|
MOUSE_STUFF(term)->drag_start_y, x, y))
|
|
{
|
|
MooTermIter iter;
|
|
int buffer_x, buffer_y;
|
|
|
|
moo_term_window_to_buffer_coords (term,
|
|
MOUSE_STUFF(term)->drag_start_x,
|
|
MOUSE_STUFF(term)->drag_start_y,
|
|
&buffer_x, &buffer_y);
|
|
|
|
moo_term_get_iter_at_location (term, &iter, buffer_x, buffer_y);
|
|
|
|
MOUSE_STUFF(term)->drag_type = DRAG_NONE;
|
|
start_selection_dnd (term, &iter, event);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
start_drag_scroll (MooTerm *term)
|
|
{
|
|
if (!MOUSE_STUFF(term)->drag_scroll_timeout)
|
|
MOUSE_STUFF(term)->drag_scroll_timeout =
|
|
g_timeout_add (SCROLL_TIMEOUT,
|
|
(GSourceFunc) drag_scroll_timeout_func,
|
|
term);
|
|
|
|
drag_scroll_timeout_func (term);
|
|
}
|
|
|
|
|
|
static void
|
|
stop_drag_scroll (MooTerm *term)
|
|
{
|
|
if (MOUSE_STUFF(term)->drag_scroll_timeout)
|
|
g_source_remove (MOUSE_STUFF(term)->drag_scroll_timeout);
|
|
MOUSE_STUFF(term)->drag_scroll_timeout = 0;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
drag_scroll_timeout_func (MooTerm *term)
|
|
{
|
|
int x, y, px, py;
|
|
MooTermIter iter;
|
|
MooTermIter start;
|
|
MooTextSelectionType t;
|
|
|
|
g_assert (MOUSE_STUFF(term)->drag_type == DRAG_SELECT);
|
|
|
|
gdk_window_get_pointer (GTK_WIDGET(term)->window, &px, &py, NULL);
|
|
moo_term_window_to_buffer_coords (term, px, py, &x, &y);
|
|
moo_term_get_iter_at_location (term, &iter, x, y);
|
|
|
|
moo_term_get_iter_at_location (term, &start,
|
|
MOUSE_STUFF(term)->drag_start_x,
|
|
MOUSE_STUFF(term)->drag_start_y);
|
|
|
|
switch (MOUSE_STUFF(term)->drag_button)
|
|
{
|
|
case GDK_BUTTON_PRESS:
|
|
t = MOO_TEXT_SELECT_CHARS;
|
|
break;
|
|
case GDK_2BUTTON_PRESS:
|
|
t = MOO_TEXT_SELECT_WORDS;
|
|
break;
|
|
default:
|
|
t = MOO_TEXT_SELECT_LINES;
|
|
}
|
|
|
|
if (_moo_term_extend_selection (term, t, &iter, &start))
|
|
_moo_term_select_range (term, &start, &iter);
|
|
else
|
|
_moo_term_select_range (term, &iter, &iter);
|
|
|
|
scroll_selection_end_onscreen (term);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
scroll_selection_end_onscreen (MooTerm *term)
|
|
{
|
|
MooTermIter iter = GET_SELECTION(term)->end;
|
|
guint top_line = term_top_line (term);
|
|
|
|
if (ITER_ROW(&iter) < (int)top_line)
|
|
moo_term_scroll_lines (term, ITER_ROW(&iter) - (int)top_line);
|
|
else if (ITER_ROW(&iter) >= (int)top_line + (int)term->priv->height)
|
|
moo_term_scroll_lines (term, ITER_ROW(&iter) + 1 - (int)top_line -
|
|
(int)term->priv->height);
|
|
}
|
|
|
|
|
|
static void
|
|
start_selection_dnd (G_GNUC_UNUSED MooTerm *term,
|
|
G_GNUC_UNUSED const MooTermIter *iter,
|
|
G_GNUC_UNUSED GdkEventMotion *event)
|
|
{
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
_moo_term_selection_empty (MooTerm *term)
|
|
{
|
|
return segment_empty (GET_SELECTION (term));
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_term_get_selected_cells (MooTerm *term,
|
|
guint *left_row,
|
|
guint *left_col,
|
|
guint *right_row,
|
|
guint *right_col)
|
|
{
|
|
Segment *selection = GET_SELECTION (term);
|
|
|
|
if (segment_empty (selection))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
MooTermIter start = selection->start;
|
|
MooTermIter end = selection->end;
|
|
|
|
_moo_term_iter_order (&start, &end);
|
|
|
|
if (left_row)
|
|
*left_row = ITER_ROW (&start);
|
|
if (left_col)
|
|
*left_col = ITER_COL (&start);
|
|
if (right_row)
|
|
*right_row = ITER_ROW (&end);
|
|
if (right_col)
|
|
*right_col = ITER_COL (&end);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
_moo_term_selection_clear (MooTerm *term)
|
|
{
|
|
MooTermIter i;
|
|
FILL_ITER (&i, term, 0, 0);
|
|
_moo_term_select_range (term, &i, &i);
|
|
}
|
|
|
|
|
|
static void
|
|
middle_button_click (MooTerm *term)
|
|
{
|
|
moo_term_paste_clipboard (term, GDK_SELECTION_PRIMARY);
|
|
}
|
|
|
|
|
|
static void
|
|
right_button_click (MooTerm *term,
|
|
GdkEventButton *event)
|
|
{
|
|
_moo_term_do_popup_menu (term, event);
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_window_to_buffer_coords (MooTerm *term,
|
|
int window_x,
|
|
int window_y,
|
|
int *buffer_x,
|
|
int *buffer_y)
|
|
{
|
|
g_return_if_fail (MOO_IS_TERM (term));
|
|
g_return_if_fail (buffer_x != NULL && buffer_y != NULL);
|
|
*buffer_x = window_x;
|
|
*buffer_y = window_y + (term_top_line (term) * term_char_height (term));
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_get_iter_at_location (MooTerm *term,
|
|
MooTermIter *iter,
|
|
int x,
|
|
int y)
|
|
{
|
|
int char_width = term_char_width (term);
|
|
int char_height = term_char_height (term);
|
|
int scrollback = buf_scrollback (term->priv->buffer);
|
|
|
|
g_return_if_fail (iter != NULL);
|
|
|
|
y /= char_height;
|
|
|
|
x = CLAMP (x, 0, (int)term->priv->width * char_width - 1);
|
|
x = (x % char_width > char_width / 2) ?
|
|
x / char_width + 1 :
|
|
x / char_width;
|
|
|
|
if (y < 0)
|
|
{
|
|
y = 0;
|
|
x = 0;
|
|
}
|
|
else if (y > scrollback + (int)term->priv->height - 1)
|
|
{
|
|
y = scrollback + (int)term->priv->height - 1;
|
|
x = term->priv->width;
|
|
}
|
|
|
|
FILL_ITER (iter, term, y, x);
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_get_iter_at_pos (MooTerm *term,
|
|
MooTermIter *iter,
|
|
int x,
|
|
int y)
|
|
{
|
|
int char_width = term_char_width (term);
|
|
int char_height = term_char_height (term);
|
|
int scrollback = buf_scrollback (term->priv->buffer);
|
|
|
|
g_return_if_fail (iter != NULL);
|
|
|
|
y /= char_height;
|
|
x /= char_width;
|
|
x = CLAMP (x, 0, (int)term->priv->width);
|
|
|
|
if (y < 0)
|
|
{
|
|
y = 0;
|
|
x = 0;
|
|
}
|
|
else if (y > scrollback + (int)term->priv->height - 1)
|
|
{
|
|
y = scrollback + (int)term->priv->height - 1;
|
|
x = term->priv->width;
|
|
}
|
|
|
|
FILL_ITER (iter, term, y, x);
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_term_get_selection_bounds (MooTerm *term,
|
|
MooTermIter *sel_start,
|
|
MooTermIter *sel_end)
|
|
{
|
|
Segment *selection = term->priv->selection;
|
|
|
|
if (sel_start)
|
|
*sel_start = selection->start;
|
|
|
|
if (sel_end)
|
|
*sel_end = selection->end;
|
|
|
|
return !_moo_term_selection_empty (term);
|
|
}
|
|
|
|
|
|
static int iter_cmp (const MooTermIter *first,
|
|
const MooTermIter *second)
|
|
{
|
|
g_assert (ITER_TERM (first) == ITER_TERM (second));
|
|
|
|
if (ITER_ROW (first) < ITER_ROW (second))
|
|
return -1;
|
|
else if (ITER_ROW (first) > ITER_ROW (second))
|
|
return 1;
|
|
else if (ITER_COL (first) < ITER_COL (second))
|
|
return -1;
|
|
else if (ITER_COL (first) > ITER_COL (second))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void iter_set_start (MooTermIter *iter)
|
|
{
|
|
ITER_ROW (iter) = ITER_COL (iter) = 0;
|
|
CHECK_ITER (iter);
|
|
}
|
|
|
|
|
|
void
|
|
_moo_term_iter_order (MooTermIter *first,
|
|
MooTermIter *second)
|
|
{
|
|
if (iter_cmp (first, second) > 0)
|
|
{
|
|
MooTermIter tmp = *first;
|
|
*first = *second;
|
|
*second = tmp;
|
|
}
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_term_iter_in_range (const MooTermIter *iter,
|
|
const MooTermIter *start,
|
|
const MooTermIter *end)
|
|
{
|
|
CHECK_ITER (iter);
|
|
CHECK_ITER (start);
|
|
CHECK_ITER (end);
|
|
return iter_cmp (start, iter) <= 0 && iter_cmp (iter, end) <= 0;
|
|
}
|
|
|
|
|
|
void
|
|
_moo_term_place_selection_end (MooTerm *term,
|
|
const MooTermIter *where)
|
|
{
|
|
CHECK_ITER (where);
|
|
_moo_term_select_range (term, &GET_SELECTION(term)->start, where);
|
|
}
|
|
|
|
|
|
void
|
|
_moo_term_get_visible_rect (MooTerm *term,
|
|
GdkRectangle *rect)
|
|
{
|
|
int char_height = term_char_height (term);
|
|
rect->x = 0;
|
|
rect->width = term->priv->width * term_char_width (term);
|
|
rect->y = term_top_line (term) * char_height;
|
|
rect->height = term->priv->height * char_height;
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_term_cell_selected (MooTerm *term,
|
|
guint row,
|
|
guint col)
|
|
{
|
|
MooTermIter start, end, iter;
|
|
|
|
if (_moo_term_get_selection_bounds (term, &start, &end))
|
|
{
|
|
_moo_term_iter_order (&start, &end);
|
|
FILL_ITER (&iter, term, row, col);
|
|
return iter_cmp (&start, &iter) <= 0 && iter_cmp (&iter, &end) < 0;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
_moo_term_row_selected (MooTerm *term,
|
|
guint row)
|
|
{
|
|
guint l_row, l_col, r_row, r_col;
|
|
|
|
if (_moo_term_get_selected_cells (term, &l_row, &l_col, &r_row, &r_col))
|
|
{
|
|
if (row < l_row || row > r_row)
|
|
return NOT_SELECTED;
|
|
else if (l_row < row && row < r_row)
|
|
return FULL_SELECTED;
|
|
else
|
|
return PART_SELECTED;
|
|
}
|
|
else
|
|
{
|
|
return NOT_SELECTED;
|
|
}
|
|
}
|
|
|
|
|
|
char *moo_term_get_selection (MooTerm *term)
|
|
{
|
|
return segment_get_text (term->priv->selection);
|
|
}
|
|
|
|
|
|
static int char_class (MooTerm *term,
|
|
const MooTermIter *iter);
|
|
static gboolean iter_ends_line (const MooTermIter *iter);
|
|
static gboolean iter_starts_line (const MooTermIter *iter);
|
|
static void iter_forward_char (MooTermIter *iter);
|
|
static void iter_backward_char (MooTermIter *iter);
|
|
static void iter_set_line_offset (MooTermIter *iter,
|
|
guint offset);
|
|
static gboolean iter_forward_line (MooTermIter *iter);
|
|
static gboolean is_space (const MooTermIter *iter);
|
|
static gboolean is_word_char (const MooTermIter *iter);
|
|
static gunichar iter_get_char (const MooTermIter *iter);
|
|
|
|
|
|
gboolean
|
|
_moo_term_extend_selection (MooTerm *term,
|
|
MooTextSelectionType type,
|
|
MooTermIter *end,
|
|
MooTermIter *start)
|
|
{
|
|
int order = iter_cmp (start, end);
|
|
|
|
CHECK_ITER (start);
|
|
CHECK_ITER (end);
|
|
|
|
if (type == MOO_TEXT_SELECT_CHARS)
|
|
{
|
|
return order;
|
|
}
|
|
|
|
if (order > 0)
|
|
{
|
|
MooTermIter *tmp = start;
|
|
start = end; end = tmp;
|
|
}
|
|
|
|
if (type == MOO_TEXT_SELECT_WORDS)
|
|
{
|
|
int ch_class;
|
|
|
|
ch_class = char_class (term, end);
|
|
|
|
while (!iter_ends_line (end) &&
|
|
char_class (term, end) == ch_class)
|
|
{
|
|
iter_forward_char (end);
|
|
}
|
|
|
|
ch_class = char_class (term, start);
|
|
|
|
while (!iter_starts_line (start) &&
|
|
char_class (term, start) == ch_class)
|
|
{
|
|
iter_backward_char (start);
|
|
}
|
|
|
|
if (char_class (term, start) != ch_class)
|
|
iter_forward_char (start);
|
|
|
|
return iter_cmp (start, end);
|
|
}
|
|
|
|
if (type == MOO_TEXT_SELECT_LINES)
|
|
{
|
|
iter_set_line_offset (start, 0);
|
|
iter_forward_line (end);
|
|
return iter_cmp (start, end);
|
|
}
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
|
|
/* TODO */
|
|
static int
|
|
char_class (MooTerm *term,
|
|
const MooTermIter *iter)
|
|
{
|
|
if (iter_ends_line (iter))
|
|
return -1;
|
|
else if (is_space (iter))
|
|
return 0;
|
|
else if (is_word_char (iter))
|
|
return 1;
|
|
else
|
|
return 2;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
iter_ends_line (const MooTermIter *iter)
|
|
{
|
|
return iter->col == (int)iter->term->priv->width;
|
|
}
|
|
|
|
|
|
static gboolean iter_starts_line (const MooTermIter *iter)
|
|
{
|
|
CHECK_ITER (iter);
|
|
return iter->col == 0;
|
|
}
|
|
|
|
|
|
static void iter_forward_char (MooTermIter *iter)
|
|
{
|
|
MooTerm *term = iter->term;
|
|
int width = term->priv->width;
|
|
int total_height = term->priv->height + buf_scrollback (term->priv->buffer);
|
|
|
|
g_return_if_fail (iter->row < total_height || iter->col < width);
|
|
|
|
if (iter->col < width)
|
|
{
|
|
iter->col++;
|
|
}
|
|
else
|
|
{
|
|
iter->col = 0;
|
|
iter->row++;
|
|
}
|
|
|
|
CHECK_ITER (iter);
|
|
}
|
|
|
|
|
|
static void iter_backward_char (MooTermIter *iter)
|
|
{
|
|
MooTerm *term = iter->term;
|
|
int width = term->priv->width;
|
|
|
|
g_return_if_fail (iter->row > 0 || iter->col > 0);
|
|
|
|
if (iter->col > 0)
|
|
{
|
|
iter->col--;
|
|
}
|
|
else
|
|
{
|
|
iter->row--;
|
|
iter->col = width;
|
|
}
|
|
|
|
CHECK_ITER (iter);
|
|
}
|
|
|
|
|
|
static void iter_set_line_offset (MooTermIter *iter,
|
|
guint offset)
|
|
{
|
|
g_return_if_fail (offset <= iter->term->priv->width);
|
|
iter->col = offset;
|
|
CHECK_ITER (iter);
|
|
}
|
|
|
|
|
|
static gboolean iter_forward_line (MooTermIter *iter)
|
|
{
|
|
MooTerm *term = iter->term;
|
|
int width = term->priv->width;
|
|
int total_height = term->priv->height + buf_scrollback (term->priv->buffer);
|
|
|
|
if (iter->row == total_height - 1)
|
|
{
|
|
iter->col = width;
|
|
CHECK_ITER (iter);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iter->col = 0;
|
|
iter->row++;
|
|
CHECK_ITER (iter);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean is_space (const MooTermIter *iter)
|
|
{
|
|
gunichar c = iter_get_char (iter);
|
|
g_return_val_if_fail (c != 0, FALSE);
|
|
return g_unichar_isspace (c);
|
|
}
|
|
|
|
|
|
static gboolean is_word_char (const MooTermIter *iter)
|
|
{
|
|
gunichar c = iter_get_char (iter);
|
|
g_return_val_if_fail (c != 0, FALSE);
|
|
return g_unichar_isalnum (c) || c == '_';
|
|
}
|
|
|
|
|
|
static gunichar iter_get_char (const MooTermIter *iter)
|
|
{
|
|
MooTermLine *line = buf_line (iter->term->priv->buffer, iter->row);
|
|
return _moo_term_line_get_char (line, iter->col);
|
|
}
|
|
|
|
|
|
static char *segment_get_text (Segment *segment)
|
|
{
|
|
MooTerm *term = ITER_TERM (&segment->start);
|
|
MooTermBuffer *buf = term->priv->buffer;
|
|
MooTermIter start = segment->start;
|
|
MooTermIter end = segment->end;
|
|
GString *text;
|
|
MooTermLine *line;
|
|
int width = term->priv->width;
|
|
int i;
|
|
|
|
_moo_term_iter_order (&start, &end);
|
|
|
|
if (!iter_cmp (&start, &end))
|
|
return NULL;
|
|
|
|
text = g_string_new ("");
|
|
|
|
if (ITER_ROW(&start) == ITER_ROW(&end))
|
|
{
|
|
line = buf_line (buf, ITER_ROW(&start));
|
|
for (i = ITER_COL(&start); i < ITER_COL(&end); ++i)
|
|
g_string_append_unichar (text, _moo_term_line_get_char (line, i));
|
|
}
|
|
else
|
|
{
|
|
if (ITER_COL(&start) < width)
|
|
{
|
|
line = buf_line (buf, ITER_ROW(&start));
|
|
for (i = ITER_COL(&start); i < width; ++i)
|
|
g_string_append_unichar (text, _moo_term_line_get_char (line, i));
|
|
g_string_append_c (text, '\n');
|
|
}
|
|
|
|
if (ITER_ROW(&start) + 1 < ITER_ROW(&end))
|
|
{
|
|
for (i = ITER_ROW(&start) + 1; i < ITER_ROW(&end); ++i)
|
|
{
|
|
int j;
|
|
line = buf_line (buf, i);
|
|
for (j = 0; j < width; ++j)
|
|
g_string_append_unichar (text, _moo_term_line_get_char (line, j));
|
|
g_string_append_c (text, '\n');
|
|
}
|
|
}
|
|
|
|
if (ITER_COL(&end) > 0)
|
|
{
|
|
line = buf_line (buf, ITER_ROW(&end));
|
|
for (i = 0; i < ITER_COL(&start); ++i)
|
|
g_string_append_unichar (text, _moo_term_line_get_char (line, i));
|
|
}
|
|
}
|
|
|
|
return g_string_free (text, FALSE);
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_select_all (MooTerm *term)
|
|
{
|
|
MooTermIter start, end;
|
|
|
|
get_start_iter (term, &start);
|
|
get_end_iter (term, &end);
|
|
|
|
_moo_term_select_range (term, &start, &end);
|
|
}
|
|
|
|
|
|
char *moo_term_get_content (MooTerm *term)
|
|
{
|
|
Segment segm;
|
|
|
|
get_start_iter (term, &segm.start);
|
|
get_end_iter (term, &segm.end);
|
|
|
|
return segment_get_text (&segm);
|
|
}
|
|
|
|
|
|
char*
|
|
moo_term_get_text (MooTermIter *start,
|
|
MooTermIter *end)
|
|
{
|
|
/* XXX check iters */
|
|
Segment segm = {*start, *end};
|
|
return segment_get_text (&segm);
|
|
}
|
|
|
|
|
|
guint
|
|
moo_term_get_line_count (MooTerm *term)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_TERM (term), 0);
|
|
return term->priv->height + buf_scrollback (term->priv->primary_buffer);
|
|
}
|
|
|
|
|
|
MooTermLine*
|
|
moo_term_get_line (MooTerm *term,
|
|
guint line_no)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_TERM (term), NULL);
|
|
g_return_val_if_fail (line_no < moo_term_get_line_count (term), NULL);
|
|
return buf_line (term->priv->primary_buffer, line_no);
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_term_get_iter_at_line (MooTerm *term,
|
|
MooTermIter *iter,
|
|
guint line)
|
|
{
|
|
g_return_val_if_fail (iter != NULL, FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM (term), FALSE);
|
|
|
|
if (line >= moo_term_get_line_count (term))
|
|
return FALSE;
|
|
|
|
FILL_ITER (iter, term, line, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_term_get_iter_at_line_offset (MooTerm *term,
|
|
MooTermIter *iter,
|
|
guint line_no,
|
|
guint offset)
|
|
{
|
|
MooTermLine *line;
|
|
|
|
g_return_val_if_fail (iter != NULL, FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM (term), FALSE);
|
|
|
|
if (line_no >= moo_term_get_line_count (term))
|
|
return FALSE;
|
|
|
|
line = buf_line (term->priv->buffer, line_no);
|
|
|
|
if (offset > _moo_term_line_len (line))
|
|
return FALSE;
|
|
|
|
FILL_ITER (iter, term, line_no, offset);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_get_iter_at_cursor (MooTerm *term,
|
|
MooTermIter *iter)
|
|
{
|
|
g_return_if_fail (MOO_IS_TERM (term));
|
|
g_return_if_fail (iter != NULL);
|
|
|
|
FILL_ITER (iter, term, buf_cursor_row_abs (term->priv->buffer),
|
|
buf_cursor_col (term->priv->buffer));
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_iter_forward_to_line_end (MooTermIter *iter)
|
|
{
|
|
MooTerm *term;
|
|
|
|
g_return_if_fail (iter != NULL);
|
|
|
|
term = ITER_TERM (iter);
|
|
g_return_if_fail (MOO_IS_TERM (term));
|
|
|
|
ITER_COL (iter) = term->priv->width;
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_apply_tag (MooTerm *term,
|
|
MooTermTag *tag,
|
|
MooTermIter *start,
|
|
MooTermIter *end)
|
|
{
|
|
MooTermBuffer *buf;
|
|
MooTermLine *line;
|
|
int i;
|
|
Segment inv;
|
|
|
|
g_return_if_fail (MOO_IS_TERM (term));
|
|
g_return_if_fail (MOO_IS_TERM_TAG (tag));
|
|
g_return_if_fail (start != NULL && end != NULL);
|
|
g_return_if_fail (ITER_TERM(start) == term && ITER_TERM(end) == term);
|
|
|
|
_moo_term_iter_order (start, end);
|
|
buf = term->priv->primary_buffer;
|
|
|
|
if (ITER_ROW (start) == ITER_ROW (end))
|
|
{
|
|
line = buf_line (buf, ITER_ROW (start));
|
|
_moo_term_line_apply_tag (line, tag, ITER_COL (start),
|
|
ITER_COL (end) - ITER_COL (start));
|
|
}
|
|
else
|
|
{
|
|
line = buf_line (buf, ITER_ROW (start));
|
|
_moo_term_line_apply_tag (line, tag, ITER_COL (start),
|
|
_moo_term_line_len (line) - ITER_COL (start));
|
|
line = buf_line (buf, ITER_ROW (end));
|
|
_moo_term_line_apply_tag (line, tag, 0, ITER_COL (end));
|
|
if (ITER_ROW (start) + 1 < ITER_ROW (end))
|
|
{
|
|
for (i = ITER_ROW (start) + 1; i < ITER_ROW (end); ++i)
|
|
{
|
|
line = buf_line (buf, i);
|
|
_moo_term_line_apply_tag (line, tag, 0,
|
|
_moo_term_line_len (line));
|
|
}
|
|
}
|
|
}
|
|
|
|
inv.start = *start;
|
|
inv.end = *end;
|
|
invalidate_segment (&inv, 1);
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_remove_tag (MooTerm *term,
|
|
MooTermTag *tag,
|
|
MooTermIter *start,
|
|
MooTermIter *end)
|
|
{
|
|
MooTermBuffer *buf;
|
|
MooTermLine *line;
|
|
Segment inv;
|
|
int i;
|
|
|
|
g_return_if_fail (MOO_IS_TERM (term));
|
|
g_return_if_fail (MOO_IS_TERM_TAG (tag));
|
|
g_return_if_fail (start != NULL && end != NULL);
|
|
g_return_if_fail (ITER_TERM(start) == term && ITER_TERM(end) == term);
|
|
|
|
_moo_term_iter_order (start, end);
|
|
buf = term->priv->primary_buffer;
|
|
|
|
if (ITER_ROW (start) == ITER_ROW (end))
|
|
{
|
|
line = buf_line (buf, ITER_ROW (start));
|
|
_moo_term_line_remove_tag (line, tag, ITER_COL (start),
|
|
ITER_COL (end) - ITER_COL (start));
|
|
}
|
|
else
|
|
{
|
|
line = buf_line (buf, ITER_ROW (start));
|
|
_moo_term_line_remove_tag (line, tag, ITER_COL (start),
|
|
_moo_term_line_len (line) - ITER_COL (start));
|
|
line = buf_line (buf, ITER_ROW (end));
|
|
_moo_term_line_remove_tag (line, tag, 0, ITER_COL (end));
|
|
if (ITER_ROW (start) + 1 < ITER_ROW (end))
|
|
{
|
|
for (i = ITER_ROW (start) + 1; i < ITER_ROW (end); ++i)
|
|
{
|
|
line = buf_line (buf, i);
|
|
_moo_term_line_remove_tag (line, tag, 0,
|
|
_moo_term_line_len (line));
|
|
}
|
|
}
|
|
}
|
|
|
|
inv.start = *start;
|
|
inv.end = *end;
|
|
invalidate_segment (&inv, 1);
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_term_iter_has_tag (MooTermIter *iter,
|
|
MooTermTag *tag)
|
|
{
|
|
MooTermBuffer *buf;
|
|
MooTerm *term;
|
|
|
|
g_return_val_if_fail (iter != NULL, FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM (ITER_TERM (iter)), FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM_TAG (tag), FALSE);
|
|
|
|
term = ITER_TERM (iter);
|
|
buf = term->priv->primary_buffer;
|
|
|
|
return _moo_term_line_has_tag (buf_line (buf, ITER_ROW (iter)),
|
|
tag, ITER_COL (iter));
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_term_iter_get_tag_start (MooTermIter *iter,
|
|
MooTermTag *tag)
|
|
{
|
|
MooTermBuffer *buf;
|
|
MooTermLine *line;
|
|
MooTerm *term;
|
|
guint col;
|
|
|
|
g_return_val_if_fail (iter != NULL, FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM (ITER_TERM (iter)), FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM_TAG (tag), FALSE);
|
|
|
|
term = ITER_TERM (iter);
|
|
buf = term->priv->primary_buffer;
|
|
line = buf_line (buf, ITER_ROW (iter));
|
|
|
|
col = ITER_COL (iter);
|
|
|
|
if (_moo_term_line_get_tag_start (line, tag, &col))
|
|
{
|
|
ITER_COL (iter) = col;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_term_iter_get_tag_end (MooTermIter *iter,
|
|
MooTermTag *tag)
|
|
{
|
|
MooTermBuffer *buf;
|
|
MooTermLine *line;
|
|
MooTerm *term;
|
|
guint col;
|
|
|
|
g_return_val_if_fail (iter != NULL, FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM (ITER_TERM (iter)), FALSE);
|
|
g_return_val_if_fail (MOO_IS_TERM_TAG (tag), FALSE);
|
|
|
|
term = ITER_TERM (iter);
|
|
buf = term->priv->primary_buffer;
|
|
line = buf_line (buf, ITER_ROW (iter));
|
|
|
|
col = ITER_COL (iter);
|
|
|
|
if (_moo_term_line_get_tag_end (line, tag, &col))
|
|
{
|
|
ITER_COL (iter) = col;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_set_line_data (MooTerm *term,
|
|
MooTermLine *line,
|
|
const char *key,
|
|
gpointer data)
|
|
{
|
|
moo_term_set_line_data_full (term, line, key, data, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
moo_term_set_line_data_full (MooTerm *term,
|
|
MooTermLine *line,
|
|
const char *key,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
g_return_if_fail (MOO_IS_TERM (term));
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (line != NULL);
|
|
|
|
_moo_term_buffer_set_line_data (term->priv->primary_buffer,
|
|
line, key, data, destroy);
|
|
}
|
|
|
|
|
|
gpointer
|
|
moo_term_get_line_data (MooTerm *term,
|
|
MooTermLine *line,
|
|
const char *key)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_TERM (term), NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
g_return_val_if_fail (line != NULL, NULL);
|
|
|
|
return _moo_term_buffer_get_line_data (term->priv->primary_buffer, line, key);
|
|
}
|
|
|
|
|
|
MooTermIter*
|
|
moo_term_iter_copy (const MooTermIter *iter)
|
|
{
|
|
g_return_val_if_fail (iter != NULL, NULL);
|
|
return g_memdup (iter, sizeof (MooTermIter));
|
|
}
|
|
|
|
void
|
|
moo_term_iter_free (MooTermIter *iter)
|
|
{
|
|
g_free (iter);
|
|
}
|
|
|
|
|
|
GType
|
|
moo_term_iter_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (!type)
|
|
type = g_boxed_type_register_static ("MooTermIter",
|
|
(GBoxedCopyFunc) moo_term_iter_copy,
|
|
(GBoxedFreeFunc) moo_term_iter_free);
|
|
return type;
|
|
}
|