Added MooText interface for handling selection with mouse

This commit is contained in:
Yevgen Muntyan 2005-07-19 09:43:44 +00:00
parent fc202e9f98
commit 6aa44ba038
12 changed files with 1197 additions and 179 deletions

View File

@ -313,12 +313,13 @@ int _moo_edit_button_press_event (GtkWidget *widget,
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1) {
GtkTextIter sel_start, sel_end;
edit->priv->drag_button = GDK_BUTTON_PRESS;
edit->priv->drag_start_x = x;
edit->priv->drag_start_y = y;
/* if clicked in selected, start drag */
GtkTextIter sel_start, sel_end;
if (gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end))
{
gtk_text_iter_order (&sel_start, &sel_end);
@ -349,25 +350,30 @@ int _moo_edit_button_press_event (GtkWidget *widget,
}
else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
{
if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
GtkTextIter bound;
if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
{
/* it may happen sometimes, if you click fast enough */
gtk_text_buffer_place_cursor (buffer, &iter);
}
edit->priv->drag_button = GDK_2BUTTON_PRESS;
edit->priv->drag_start_x = x;
edit->priv->drag_start_y = y;
edit->priv->drag_type = MOO_EDIT_DRAG_SELECT;
GtkTextIter bound = iter;
bound = iter;
if (extend_selection (edit, MOO_EDIT_SELECT_WORDS, &iter, &bound))
gtk_text_buffer_select_range (buffer, &iter, &bound);
}
else if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
{
GtkTextIter bound = iter;
edit->priv->drag_button = GDK_3BUTTON_PRESS;
edit->priv->drag_start_x = x;
edit->priv->drag_start_y = y;
edit->priv->drag_type = MOO_EDIT_DRAG_SELECT;
GtkTextIter bound = iter;
if (extend_selection (edit, MOO_EDIT_SELECT_LINES, &iter, &bound))
gtk_text_buffer_select_range (buffer, &iter, &bound);
}
@ -380,6 +386,7 @@ int _moo_edit_button_release_event (GtkWidget *widget,
{
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
MooEdit *edit = MOO_EDIT (widget);
GtkTextIter iter;
switch (edit->priv->drag_type) {
case MOO_EDIT_DRAG_NONE:
@ -398,7 +405,6 @@ int _moo_edit_button_release_event (GtkWidget *widget,
/* if we were really dragging, drop it
* otherwise, it was just a single click in selected text */
g_assert (!edit->priv->drag_moved); /* parent should handle drag */
GtkTextIter iter;
gtk_text_view_get_iter_at_location (text_view, &iter,
edit->priv->drag_start_x,
edit->priv->drag_start_y);

View File

@ -158,6 +158,7 @@ void moo_term_invalidate_rect (MooTerm *term,
gdk_window_invalidate_rect (GTK_WIDGET(term)->window,
&r, FALSE);
moo_term_invalidate_content_rect (term, rect);
add_update_timeout (term);
}
@ -525,7 +526,6 @@ static void term_draw_range (MooTerm *term,
guint start,
guint len)
{
TermSelection *sel = term->priv->selection;
int selected;
guint first = start;
guint last = start + len;
@ -555,7 +555,7 @@ static void term_draw_range (MooTerm *term,
}
}
selected = term_selection_row_selected (sel, abs_row);
selected = moo_term_selection_row_selected (term, abs_row);
switch (selected)
{
@ -566,10 +566,10 @@ static void term_draw_range (MooTerm *term,
case PART_SELECTED:
{
guint l_row = sel->l_row;
guint l_col = sel->l_col;
guint r_row = sel->r_row;
guint r_col = sel->r_col;
guint l_row, l_col, r_row, r_col;
moo_term_get_selection_bounds (term, &l_row, &l_col,
&r_row, &r_col);
if (l_row == r_row)
{
@ -806,7 +806,6 @@ static void term_draw_cursor (MooTerm *term)
guint abs_row = buf_cursor_row_abs (term->priv->buffer);
guint col = buf_cursor_col (term->priv->buffer);
MooTermLine *line = buf_line (term->priv->buffer, abs_row);
TermSelection *sel = term->priv->selection;
char ch[6];
guint ch_len;
int screen_row = abs_row - term_top_line (term);
@ -852,7 +851,7 @@ static void term_draw_cursor (MooTerm *term)
bg = term->priv->bg[CURSOR][MOO_TERM_COLOR_MAX];
}
if (!term_selected (sel, abs_row, col))
if (!moo_term_selected (term, abs_row, col))
{
gdk_draw_rectangle (term->priv->back_pixmap,
bg,
@ -889,7 +888,7 @@ static void term_draw_cursor (MooTerm *term)
break;
case CURSOR_UNDERLINE:
if (!term_selected (sel, abs_row, col))
if (!moo_term_selected (term, abs_row, col))
{
gdk_draw_rectangle (term->priv->back_pixmap,
term->priv->bg[NORMAL][MOO_TERM_COLOR_MAX],
@ -1031,7 +1030,6 @@ void moo_term_invert_colors (MooTerm *term,
{
if (invert != term->priv->colors_inverted)
{
moo_term_invalidate_content_all (term);
moo_term_invalidate_all (term);
term->priv->colors_inverted = invert;
}

View File

@ -330,7 +330,7 @@ void moo_term_set_mouse_tracking (MooTerm *term,
term->priv->tracking_mouse = tracking_type;
moo_term_update_pointer (term);
stop_mouse_tracking (term);
term_selection_clear (term);
moo_term_selection_clear (term);
switch (tracking_type)
{

View File

@ -68,7 +68,6 @@ typedef enum {
typedef struct _TermFontInfo TermFontInfo;
typedef struct _TermSelection TermSelection;
struct _MooTermPrivate {
@ -101,8 +100,7 @@ struct _MooTermPrivate {
int single_shift;
} saved_cursor;
TermSelection *selection;
gpointer selection;
TermFontInfo *font_info;
GdkPixmap *back_pixmap;
@ -150,6 +148,7 @@ struct _MooTermPrivate {
(term)->priv->_top_line : \
buf_scrollback ((term)->priv->buffer))
void moo_term_text_iface_init (gpointer iface);
void moo_term_set_window_title (MooTerm *term,
const char *title);

View File

@ -13,108 +13,538 @@
#define MOOTERM_COMPILATION
#include "mooterm/mooterm-private.h"
#include "mooterm/mootermbuffer-private.h"
#include "mooterm/mooterm-selection.h"
#include "mooui/mootext.h"
TermSelection *term_selection_new (void)
typedef struct {
GtkTextIter start;
GtkTextIter end;
} Segment;
#define GET_SELECTION(term) ((Segment*)(term)->priv->selection)
#define ITER_ROW(iter) ((iter)->dummy3)
#define ITER_COL(iter) ((iter)->dummy4)
#define ITER_TERM(iter) ((MooTerm*)(iter)->dummy1)
#define ITER_SET_TERM(iter, term) (iter)->dummy1 = term
#define FILL_ITER(iter, term, row, col) \
(iter)->dummy1 = term; \
ITER_ROW(iter) = row; \
ITER_COL(iter) = col;
static int iter_cmp (const GtkTextIter *first,
const GtkTextIter *second);
static void iter_order (GtkTextIter *first,
GtkTextIter *second);
static void iter_set_start (GtkTextIter *iter);
gpointer term_selection_new (MooTerm *term)
{
TermSelection *sel = g_new0 (TermSelection, 1);
Segment *sel = g_new0 (Segment, 1);
sel->screen_width = 0;
sel->button_pressed = FALSE;
sel->click = 1;
sel->drag = FALSE;
sel->scroll = SELECT_SCROLL_NONE;
sel->st_id = 0;
sel->empty = TRUE;
FILL_ITER (&sel->start, term, 0, 0);
FILL_ITER (&sel->end, term, 0, 0);
return sel;
}
inline static void reset_aux_stuff (TermSelection *sel)
static gboolean segment_empty (Segment *s)
{
sel->button_pressed = FALSE;
sel->click = 1;
sel->drag = FALSE;
return !iter_cmp (&s->start, &s->end);
}
void term_selection_clear (MooTerm *term)
static int segment_sym_diff (Segment *s1, Segment *s2,
Segment *result)
{
if (!term->priv->selection->empty)
Segment *left = result;
Segment *right = &result[1];
if (segment_empty (s1))
{
term->priv->selection->empty = TRUE;
moo_term_invalidate_all (term);
if (segment_empty (s2))
{
return 0;
}
else
{
*left = *s2;
return 1;
}
}
else if (segment_empty (s2))
{
*left = *s1;
return 1;
}
else
{
Segment tmp;
GtkTextIter itmp;
*left = *s1;
*right = *s2;
iter_order (&left->start, &left->end);
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;
return 1;
case -1:
left->start = left->end;
left->end = right->end;
return 1;
}
case 1:
tmp = *left;
*left = *right;
*right = tmp;
break;
}
switch (iter_cmp (&left->end, &right->start))
{
case 0:
left->end = right->end;
return 1;
case -1:
return 2;
case 1:
itmp = left->end;
left->end = right->start;
right->start = itmp;
return 2;
}
}
g_assert_not_reached ();
}
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)
{
GtkTextIter start = segm[i].start;
GtkTextIter end = segm[i].end;
GdkRectangle rect;
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);
}
}
}
}
char *term_selection_get_text (MooTerm *term)
static void term_select_range (const GtkTextIter *start,
const GtkTextIter *end)
{
return NULL;
Segment diff[2];
Segment new_selection;
Segment old_selection;
old_selection = *GET_SELECTION (ITER_TERM (start));
ITER_SET_TERM (&new_selection.start, ITER_TERM (start));
ITER_SET_TERM (&new_selection.end, ITER_TERM (start));
new_selection.start = *start;
new_selection.end = *end;
if (!iter_cmp (start, end))
{
iter_set_start (&new_selection.start);
iter_set_start (&new_selection.end);
}
GET_SELECTION (ITER_TERM (start))->start = new_selection.start;
GET_SELECTION (ITER_TERM (start))->end = new_selection.end;
invalidate_segment (diff,
segment_sym_diff (&new_selection,
&old_selection,
diff));
}
#define HOWMANY(x, y) (((x) + (y) - 1) / y())
#define CALC_ROW(y, char_height) (HOWMANY (y + 1, char_height) - 1)
#define CALC_COL(x, char_width) (HOWMANY (x + 1, char_width) - 1)
#define DIFF(x, y) (ABS ((x) - (y)))
gboolean moo_term_button_press (GtkWidget *widget,
GdkEventButton *event)
{
MooTerm *term;
term = MOO_TERM (widget);
moo_term_set_pointer_visible (term, TRUE);
if (event->button != 1)
{
if (event->type != GDK_BUTTON_PRESS)
return TRUE;
switch (event->button)
{
case 2:
moo_term_paste_clipboard (term, GDK_SELECTION_PRIMARY);
break;
case 3:
moo_term_do_popup_menu (term, event);
break;
}
return TRUE;
}
return TRUE;
moo_term_set_pointer_visible (MOO_TERM (widget), TRUE);
return moo_text_button_press_event (widget, event);
}
gboolean moo_term_button_release (GtkWidget *widget,
GdkEventButton *event)
{
MooTerm *term;
term = MOO_TERM (widget);
moo_term_set_pointer_visible (term, TRUE);
return TRUE;
moo_term_set_pointer_visible (MOO_TERM (widget), TRUE);
return moo_text_button_release_event (widget, event);
}
gboolean moo_term_motion_notify (GtkWidget *widget,
GdkEventMotion *event)
{
MooTerm *term;
term = MOO_TERM (widget);
moo_term_set_pointer_visible (term, TRUE);
return TRUE;
moo_term_set_pointer_visible (MOO_TERM (widget), TRUE);
return moo_text_motion_event (widget, event);
}
gboolean moo_term_selection_empty (MooTerm *term)
{
return segment_empty (GET_SELECTION (term));
}
gboolean moo_term_get_selection_bounds (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
{
if (iter_cmp (&selection->start, &selection->end) > 0)
{
*left_row = ITER_ROW (&selection->end);
*left_col = ITER_COL (&selection->end);
*right_row = ITER_ROW (&selection->start);
*right_col = ITER_COL (&selection->start);
}
else
{
*left_row = ITER_ROW (&selection->start);
*left_col = ITER_COL (&selection->start);
*right_row = ITER_ROW (&selection->end);
*right_col = ITER_COL (&selection->end);
}
return TRUE;
}
}
void moo_term_selection_clear (MooTerm *term)
{
GtkTextIter i;
FILL_ITER (&i, term, 0, 0);
term_select_range (&i, &i);
}
static void middle_button_click (MooText *obj,
G_GNUC_UNUSED GdkEventButton *event)
{
moo_term_paste_clipboard (MOO_TERM (obj), GDK_SELECTION_PRIMARY);
}
static void right_button_click (MooText *obj,
GdkEventButton *event)
{
moo_term_do_popup_menu (MOO_TERM (obj), event);
}
static gboolean extend_selection (MooText *obj,
MooTextSelectionType type,
GtkTextIter *insert,
GtkTextIter *selection_bound)
{
term_select_range (selection_bound, insert);
return iter_cmp (selection_bound, insert);
}
static void window_to_buffer_coords (MooText *obj,
int window_x,
int window_y,
int *buffer_x,
int *buffer_y)
{
MooTerm *term = MOO_TERM (obj);
*buffer_x = window_x;
*buffer_y = window_y + (term_top_line (term) * term_char_height (term));
}
static void get_iter_at_location (MooText *obj,
GtkTextIter *iter,
int x,
int y)
{
MooTerm *term = MOO_TERM (obj);
int char_width = term_char_width (term);
int char_height = term_char_height (term);
int scrollback = buf_scrollback (term->priv->buffer);
guint row, col;
g_return_if_fail (iter != NULL);
y /= char_height;
y = CLAMP (y, 0, scrollback + (int)term->priv->height - 1);
row = y;
x = CLAMP (x, 0, (int)term->priv->width * char_width - 1);
col = (x % char_width > char_width / 2) ?
x / char_width + 1 :
x / char_width;
FILL_ITER (iter, term, row, col);
}
static gboolean get_selection_bounds (MooText *obj,
GtkTextIter *sel_start,
GtkTextIter *sel_end)
{
MooTerm *term = MOO_TERM (obj);
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 GtkTextIter *first,
const GtkTextIter *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 (GtkTextIter *iter)
{
ITER_ROW (iter) = ITER_COL (iter) = 0;
}
static void iter_order (GtkTextIter *first,
GtkTextIter *second)
{
if (iter_cmp (first, second) > 0)
{
GtkTextIter tmp = *first;
*first = *second;
*second = tmp;
}
}
static gboolean iter_in_range (const GtkTextIter *iter,
const GtkTextIter *start,
const GtkTextIter *end)
{
return iter_cmp (start, iter) <= 0 && iter_cmp (iter, end) <= 0;
}
static void select_range (G_GNUC_UNUSED MooText *obj,
const GtkTextIter *start,
const GtkTextIter *end)
{
term_select_range (start, end);
}
static void place_selection_end (MooText *obj,
const GtkTextIter *where)
{
MooTerm *term = MOO_TERM (obj);
term_select_range (&GET_SELECTION(term)->start, where);
}
static void scroll_selection_end_onscreen (MooText *obj)
{
MooTerm *term = MOO_TERM (obj);
GtkTextIter 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 GdkWindow* get_window (MooText *obj)
{
return GTK_WIDGET(obj)->window;
}
static void get_visible_rect (MooText *obj,
GdkRectangle *rect)
{
MooTerm *term = MOO_TERM (obj);
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_selected (MooTerm *term,
guint row,
guint col)
{
GtkTextIter start, end, iter;
if (get_selection_bounds (MOO_TEXT (term), &start, &end))
{
iter_order (&start, &end);
FILL_ITER (&iter, term, row, col);
return iter_in_range (&iter, &start, &end);
}
else
{
return FALSE;
}
}
int moo_term_selection_row_selected (MooTerm *term,
guint row)
{
GtkTextIter start, end;
if (get_selection_bounds (MOO_TEXT (term), &start, &end))
{
iter_order (&start, &end);
if (ITER_ROW (&end) < (int)row || (int)row < ITER_ROW (&start))
return NOT_SELECTED;
else if (ITER_ROW (&start) < (int)row && (int)row < ITER_ROW (&end))
return FULL_SELECTED;
else
return PART_SELECTED;
}
else
{
return FALSE;
}
}
void moo_term_text_iface_init (gpointer g_iface)
{
MooTextIface *iface = (MooTextIface*) g_iface;
iface->start_selection_dnd = NULL;
iface->middle_button_click = middle_button_click;
iface->right_button_click = right_button_click;
iface->extend_selection = extend_selection;
iface->window_to_buffer_coords = window_to_buffer_coords;
iface->get_iter_at_location = get_iter_at_location;
iface->get_selection_bounds = get_selection_bounds;
iface->iter_order = iter_order;
iface->iter_in_range = iter_in_range;
iface->place_selection_end = place_selection_end;
iface->select_range = select_range;
iface->scroll_selection_end_onscreen = scroll_selection_end_onscreen;
iface->get_window = get_window;
iface->get_visible_rect = get_visible_rect;
}
char *moo_term_selection_get_text (MooTerm *term)
{
return NULL;
}

View File

@ -14,7 +14,7 @@
#ifndef MOOTERM_MOOTERM_SELECTION_H
#define MOOTERM_MOOTERM_SELECTION_H
#include <glib.h>
#include <mooterm/mooterm.h>
G_BEGIN_DECLS
@ -25,82 +25,24 @@ enum {
PART_SELECTED = 2
};
#define SELECT_SCROLL_NONE (0)
#define SELECT_SCROLL_UP (-1)
#define SELECT_SCROLL_DOWN (1)
struct _TermSelection {
guint screen_width;
gboolean moo_term_selection_empty (MooTerm *term);
/* absolute coordinates in the buffer
selected range is [(l_row, l_col), (r_row, r_col))
l_row, l_col and r_row are valid
r_col may be equal to _width */
guint l_row;
guint l_col;
guint r_row;
guint r_col;
gboolean empty;
gboolean button_pressed;
int click;
gboolean drag;
// buffer coordinates
guint drag_begin_row;
guint drag_begin_col;
guint drag_end_row;
guint drag_end_col;
int scroll;
guint st_id;
};
gpointer term_selection_new (MooTerm *term);
TermSelection *term_selection_new (void);
#define term_selection_free(sel) g_free (sel)
void term_set_selection (struct _MooTerm *term,
guint row1,
guint col1,
guint row2,
guint col2);
void term_selection_clear (struct _MooTerm *term);
char *term_selection_get_text (struct _MooTerm *term);
inline static void term_selection_set_width (struct _MooTerm *term,
guint width)
{
term->priv->selection->screen_width = width;
term_selection_clear (term);
}
inline static int term_selection_row_selected (TermSelection *sel,
guint row)
{
if (sel->empty || sel->r_row < row || row < sel->l_row)
return NOT_SELECTED;
else if (sel->l_row < row && row < sel->r_row)
return FULL_SELECTED;
else
return PART_SELECTED;
}
inline static gboolean term_selected (TermSelection *sel,
guint row,
guint col)
{
if (sel->empty || sel->r_row < row || row < sel->l_row)
return FALSE;
else if (sel->l_row < row && row < sel->r_row)
return TRUE;
else if (sel->l_row == sel->r_row)
return sel->l_col <= col && col < sel->r_col;
else if (sel->l_row == row)
return sel->l_col <= col;
else
return col < sel->r_row;
}
void moo_term_selection_clear (MooTerm *term);
char *moo_term_selection_get_text (MooTerm *term);
int moo_term_selection_row_selected (MooTerm *term,
guint row);
gboolean moo_term_selected (MooTerm *term,
guint row,
guint col);
gboolean moo_term_get_selection_bounds (MooTerm *term,
guint *left_row,
guint *left_col,
guint *right_row,
guint *right_col);
G_END_DECLS

View File

@ -17,10 +17,13 @@
#include "mooterm/mootermparser.h"
#include "mooterm/mootermpt.h"
#include "mooterm/mootermbuffer-private.h"
#include "mooui/mootext.h"
#include "mooutils/moocompat.h"
#include "mooutils/moomarshals.h"
static void moo_term_class_init (MooTermClass *klass);
static void moo_term_init (MooTerm *term);
static void moo_term_finalize (GObject *object);
static void moo_term_realize (GtkWidget *widget);
@ -83,7 +86,44 @@ enum {
/* MOO_TYPE_TERM */
G_DEFINE_TYPE (MooTerm, moo_term, GTK_TYPE_WIDGET)
static gpointer moo_term_parent_class = NULL;
GType moo_term_get_type (void)
{
static GType type = 0;
if (!type)
{
static const GTypeInfo info = {
sizeof (MooTermClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) moo_term_class_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (MooTerm),
0, /* n_preallocs */
(GInstanceInitFunc) moo_term_init,
NULL /* value_table */
};
static const GInterfaceInfo iface_info = {
(GInterfaceInitFunc) moo_term_text_iface_init,
NULL,
NULL
};
type = g_type_register_static (GTK_TYPE_WIDGET, "MooTerm",
&info, (GTypeFlags) 0);
g_type_add_interface_static (type,
MOO_TYPE_TEXT,
&iface_info);
}
return type;
}
static guint signals[LAST_SIGNAL];
@ -92,6 +132,8 @@ static void moo_term_class_init (MooTermClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
moo_term_parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = moo_term_finalize;
widget_class->realize = moo_term_realize;
@ -188,7 +230,7 @@ static void moo_term_init (MooTerm *term)
term->priv->width = 80;
term->priv->height = 24;
term->priv->selection = term_selection_new ();
term->priv->selection = term_selection_new (term);
term->priv->cursor_visible = TRUE;
@ -261,7 +303,7 @@ static void moo_term_finalize (GObject *object)
g_object_unref (term->priv->primary_buffer);
g_object_unref (term->priv->alternate_buffer);
term_selection_free (term->priv->selection);
g_free (term->priv->selection);
term_font_info_free (term->priv->font_info);
g_object_unref (term->priv->back_pixmap);
@ -464,7 +506,7 @@ static void scrollback_changed (MooTerm *term,
if (term->priv->_scrolled && term->priv->_top_line > scrollback)
{
term_selection_clear (term);
moo_term_selection_clear (term);
scroll_to_bottom (term, TRUE);
}
else
@ -485,8 +527,7 @@ static void width_changed (MooTerm *term,
if (GTK_WIDGET_REALIZED (term))
moo_term_resize_back_pixmap (term);
term_selection_set_width (term, term->priv->width);
term_selection_clear (term);
moo_term_selection_clear (term);
}
@ -507,7 +548,7 @@ static void height_changed (MooTerm *term,
if (GTK_WIDGET_REALIZED (term))
moo_term_resize_back_pixmap (term);
term_selection_clear (term);
moo_term_selection_clear (term);
moo_term_invalidate_all (term);
}
@ -667,7 +708,6 @@ static void scroll_abs (MooTerm *term,
term->priv->_top_line = line;
term->priv->_scrolled = TRUE;
moo_term_invalidate_content_all (term);
moo_term_invalidate_all (term);
if (update_adj)
@ -696,7 +736,6 @@ static void scroll_to_bottom (MooTerm *term,
term->priv->_scrolled = FALSE;
moo_term_invalidate_content_all (term);
moo_term_invalidate_all (term);
if (update_full)
@ -759,7 +798,7 @@ void moo_term_copy_clipboard (MooTerm *term,
GtkClipboard *cb;
char *text;
text = term_selection_get_text (term);
text = moo_term_selection_get_text (term);
if (text && *text)
{
@ -1176,7 +1215,6 @@ void moo_term_set_alternate_buffer (MooTerm *term,
else
term->priv->buffer = term->priv->primary_buffer;
moo_term_invalidate_content_all (term);
moo_term_invalidate_all (term);
moo_term_buffer_scrollback_changed (term->priv->buffer);
}
@ -1414,16 +1452,44 @@ static void menu_position_func (G_GNUC_UNUSED GtkMenu *menu,
}
static void menu_copy (MooTerm *term)
{
moo_term_copy_clipboard (term, GDK_SELECTION_CLIPBOARD);
}
static void menu_paste (MooTerm *term)
{
moo_term_paste_clipboard (term, GDK_SELECTION_CLIPBOARD);
}
static void destroy_menu (GtkWidget *menu)
{
g_idle_add ((GSourceFunc)gtk_widget_destroy, menu);
}
void moo_term_do_popup_menu (MooTerm *term,
GdkEventButton *event)
{
GtkWidget *menu;
GtkWidget *item;
menu = gtk_menu_new ();
/* TODO: wtf? */
g_signal_connect (menu, "deactivate",
G_CALLBACK (gtk_widget_destroy), NULL);
G_CALLBACK (destroy_menu), NULL);
item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL);
gtk_widget_show (item);
g_signal_connect_swapped (item, "activate",
G_CALLBACK (menu_copy), term);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
item = gtk_image_menu_item_new_from_stock (GTK_STOCK_PASTE, NULL);
gtk_widget_show (item);
g_signal_connect_swapped (item, "activate",
G_CALLBACK (menu_paste), term);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
/* TODO: add copy/paste */
g_signal_emit (term, signals[POPULATE_POPUP], 0, menu);
if (event)

View File

@ -28,6 +28,8 @@ libmooui_la_SOURCES = \
mooshortcutsprefs-glade.c \
mooshortcutsprefs.c \
mooshortcutsprefs.h \
mootext.c \
mootext.h \
mootoggleaction.c \
mootoggleaction.h \
moouiobject.c \

474
moo/mooui/mootext.c Normal file
View File

@ -0,0 +1,474 @@
/*
* mooui/mootext.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.
*/
#include "mooui/mootext.h"
typedef struct _MooTextData MooTextData;
struct _MooTextData {
guint drag_scroll_timeout;
GdkEventType drag_button;
MooTextDragType drag_type;
int drag_start_x;
int drag_start_y;
guint drag_moved : 1;
guint double_click_selects_brackets : 1;
guint double_click_selects_inside : 1;
};
static const GQuark *get_quark (void) G_GNUC_CONST;
static MooTextData *moo_text_data_new (void);
static MooTextData *moo_text_data_clear (MooTextData *data);
static void moo_text_data_free (MooTextData *data);
static MooTextData *moo_text_get_data (MooText *obj);
#define MOO_TEXT_DATA_QUARK (get_quark()[0])
#define GET_DATA(obj) (moo_text_get_data (obj))
typedef gboolean (*ExtendSelectionFunc) (MooText *obj,
MooTextSelectionType type,
GtkTextIter *insert,
GtkTextIter *selection_bound);
typedef void (*StartSelectionDndFunc) (MooText *obj,
const GtkTextIter *iter,
GdkEventMotion *event);
static MooTextData *moo_text_data_new (void)
{
return moo_text_data_clear (g_new0 (MooTextData, 1));
}
static MooTextData *moo_text_data_clear (MooTextData *data)
{
data->drag_moved = FALSE;
data->drag_type = MOO_TEXT_DRAG_NONE;
data->drag_start_x = -1;
data->drag_start_y = -1;
data->drag_button = GDK_BUTTON_RELEASE;
return data;
}
static void moo_text_data_free (MooTextData *data)
{
g_free (data);
}
/**************************************************************************/
/* mouse functionality
*/
#define get_impl(func) MOO_TEXT_GET_IFACE(obj)->func
static gboolean can_drag_selection (MooText *obj)
{
return MOO_TEXT_GET_IFACE(obj)->start_selection_dnd != NULL;
}
static void start_drag_scroll (MooText *obj);
static void stop_drag_scroll (MooText *obj);
static gboolean drag_scroll_timeout_func (MooText *obj);
static const int SCROLL_TIMEOUT = 100;
gboolean moo_text_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
MooText *obj;
GtkTextIter iter;
MooTextData *data;
int x, y;
obj = MOO_TEXT (widget);
data = moo_text_get_data (obj);
gtk_widget_grab_focus (widget);
get_impl(window_to_buffer_coords) (obj, event->x, event->y, &x, &y);
get_impl(get_iter_at_location) (obj, &iter, x, y);
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1)
{
GtkTextIter sel_start, sel_end;
data->drag_button = GDK_BUTTON_PRESS;
data->drag_start_x = x;
data->drag_start_y = y;
/* if clicked in selected, start drag */
if (get_impl(get_selection_bounds) (obj, &sel_start, &sel_end))
{
get_impl(iter_order) (&sel_start, &sel_end);
if (get_impl(iter_in_range) (&iter, &sel_start, &sel_end)
&& can_drag_selection (obj))
{
/* clicked inside of selection,
* set up drag and return */
data->drag_type = MOO_TEXT_DRAG_DRAG;
return TRUE;
}
}
/* otherwise, clear selection, and position cursor at clicked point */
if (event->state & GDK_SHIFT_MASK)
{
get_impl(place_selection_end) (obj, &iter);
}
else
{
get_impl(select_range) (obj, &iter, &iter);
}
data->drag_type = MOO_TEXT_DRAG_SELECT;
}
else if (event->button == 2)
{
get_impl(middle_button_click) (obj, event);
}
else if (event->button == 3)
{
get_impl(right_button_click) (obj, event);
}
else
{
g_warning ("got button %d in button_press callback", event->button);
}
}
else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
{
GtkTextIter bound;
if (get_impl(get_selection_bounds) (obj, NULL, NULL))
{
/* it may happen sometimes, if you click fast enough */
get_impl(select_range) (obj, &iter, &iter);
}
data->drag_button = GDK_2BUTTON_PRESS;
data->drag_start_x = x;
data->drag_start_y = y;
data->drag_type = MOO_TEXT_DRAG_SELECT;
bound = iter;
if (get_impl(extend_selection) (obj, MOO_TEXT_SELECT_WORDS, &iter, &bound))
get_impl(select_range) (obj, &bound, &iter);
}
else if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
{
GtkTextIter bound;
data->drag_button = GDK_3BUTTON_PRESS;
data->drag_start_x = x;
data->drag_start_y = y;
data->drag_type = MOO_TEXT_DRAG_SELECT;
bound = iter;
if (get_impl(extend_selection) (obj, MOO_TEXT_SELECT_LINES, &iter, &bound))
get_impl(select_range) (obj, &bound, &iter);
}
return TRUE;
}
gboolean moo_text_button_release_event (GtkWidget *widget,
G_GNUC_UNUSED GdkEventButton *event)
{
MooText *obj = MOO_TEXT (widget);
MooTextData *data = moo_text_get_data (obj);
GtkTextIter iter;
switch (data->drag_type)
{
case MOO_TEXT_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 MOO_TEXT_DRAG_SELECT:
/* everything should be done already in button_press and
* motion_notify handlers */
stop_drag_scroll (obj);
break;
case MOO_TEXT_DRAG_DRAG:
/* if we were really dragging, drop it
* otherwise, it was just a single click in selected text */
g_assert (!data->drag_moved); /* parent should handle drag */ /* TODO ??? */
get_impl(get_iter_at_location) (obj, &iter,
data->drag_start_x,
data->drag_start_y);
get_impl(select_range) (obj, &iter, &iter);
break;
default:
g_assert_not_reached ();
}
moo_text_data_clear (data);
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_text_motion_event (GtkWidget *widget,
GdkEventMotion *event)
{
MooText *obj = MOO_TEXT (widget);
MooTextData *data = moo_text_get_data (obj);
int x, y, event_x, event_y;
GtkTextIter iter;
if (!data->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;
}
get_impl(window_to_buffer_coords) (obj, event_x, event_y, &x, &y);
get_impl(get_iter_at_location) (obj, &iter, x, y);
if (data->drag_type == MOO_TEXT_DRAG_SELECT)
{
GdkRectangle rect;
GtkTextIter start;
MooTextSelectionType t;
data->drag_moved = TRUE;
get_impl(get_visible_rect) (obj, &rect);
if (outside (x, y, rect))
{
start_drag_scroll (obj);
return TRUE;
}
else
{
stop_drag_scroll (obj);
}
get_impl(get_iter_at_location) (obj, &start,
data->drag_start_x,
data->drag_start_y);
switch (data->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 (get_impl(extend_selection) (obj, t, &iter, &start))
get_impl(select_range) (obj, &start, &iter);
else
get_impl(select_range) (obj, &iter, &iter);
}
else
{
/* this piece is from gtktextview.c */
int x, y;
gdk_window_get_pointer (get_impl(get_window) (obj), &x, &y, NULL);
if (gtk_drag_check_threshold (widget,
data->drag_start_x,
data->drag_start_y,
x, y))
{
GtkTextIter iter;
int buffer_x, buffer_y;
get_impl(window_to_buffer_coords) (obj,
data->drag_start_x,
data->drag_start_y,
&buffer_x,
&buffer_y);
get_impl(get_iter_at_location) (obj, &iter, buffer_x, buffer_y);
data->drag_type = MOO_TEXT_DRAG_NONE;
get_impl(start_selection_dnd) (obj, &iter, event);
}
}
return TRUE;
}
static void start_drag_scroll (MooText *obj)
{
MooTextData *data = moo_text_get_data (obj);
if (!data->drag_scroll_timeout)
data->drag_scroll_timeout =
g_timeout_add (SCROLL_TIMEOUT,
(GSourceFunc)drag_scroll_timeout_func,
obj);
drag_scroll_timeout_func (obj);
}
static void stop_drag_scroll (MooText *obj)
{
MooTextData *data = moo_text_get_data (obj);
if (data->drag_scroll_timeout)
g_source_remove (data->drag_scroll_timeout);
data->drag_scroll_timeout = 0;
}
static gboolean drag_scroll_timeout_func (MooText *obj)
{
int x, y, px, py;
GtkTextIter iter;
GtkTextIter start;
MooTextSelectionType t;
MooTextData *data = moo_text_get_data (obj);
g_assert (data->drag_type == MOO_TEXT_DRAG_SELECT);
gdk_window_get_pointer (get_impl(get_window) (obj), &px, &py, NULL);
get_impl(window_to_buffer_coords) (obj, px, py, &x, &y);
get_impl(get_iter_at_location) (obj, &iter, x, y);
get_impl(get_iter_at_location) (obj, &start,
data->drag_start_x,
data->drag_start_y);
switch (data->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 (get_impl(extend_selection) (obj, t, &iter, &start))
get_impl(select_range) (obj, &start, &iter);
else
get_impl(select_range) (obj, &iter, &iter);
get_impl(scroll_selection_end_onscreen) (obj);
return TRUE;
}
/**************************************************************************/
/* GInterface stuff
*/
static void moo_text_iface_init (gpointer g_iface);
GType moo_text_get_type (void)
{
static GType type = 0;
if (!type)
{
static const GTypeInfo info =
{
sizeof (MooTextIface), /* class_size */
moo_text_iface_init, /* base_init */
NULL, /* base_finalize */
NULL, /* class_init */
0, 0, 0, 0, 0, 0
};
type = g_type_register_static (G_TYPE_INTERFACE,
"MooText",
&info, (GTypeFlags)0);
}
return type;
}
static void moo_text_iface_init (G_GNUC_UNUSED gpointer g_iface)
{
}
static const GQuark *get_quark (void)
{
static GQuark q[1] = {0};
if (!q[0])
{
q[0] = g_quark_from_static_string ("moo_text_data");
}
return q;
}
static MooTextData *moo_text_get_data (MooText *obj)
{
MooTextData *data;
g_return_val_if_fail (MOO_IS_TEXT (obj), NULL);
data = g_object_get_qdata (G_OBJECT (obj), MOO_TEXT_DATA_QUARK);
if (!data)
{
data = moo_text_data_new ();
g_object_set_qdata_full (G_OBJECT (obj),
MOO_TEXT_DATA_QUARK,
data,
(GDestroyNotify) moo_text_data_free);
}
return data;
}
/**************************************************************************/
/* GInterface stuff
*/

101
moo/mooui/mootext.h Normal file
View File

@ -0,0 +1,101 @@
/*
* mooui/mootext.h
*
* 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.
*/
#ifndef MOOUI_MOOTEXT_H
#define MOOUI_MOOTEXT_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define MOO_TYPE_TEXT (moo_text_get_type ())
#define MOO_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOO_TYPE_TEXT, MooText))
#define MOO_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOO_TYPE_TEXT))
#define MOO_TEXT_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MOO_TYPE_TEXT, MooTextIface))
typedef struct _MooText MooText;
typedef struct _MooTextIface MooTextIface;
typedef enum {
MOO_TEXT_DRAG_NONE = 0,
MOO_TEXT_DRAG_SELECT,
MOO_TEXT_DRAG_DRAG
} MooTextDragType;
typedef enum {
MOO_TEXT_SELECT_CHARS,
MOO_TEXT_SELECT_WORDS,
MOO_TEXT_SELECT_LINES
} MooTextSelectionType;
struct _MooTextIface {
GTypeInterface parent;
void (*middle_button_click) (MooText *obj,
GdkEventButton *event);
void (*right_button_click) (MooText *obj,
GdkEventButton *event);
void (*start_selection_dnd) (MooText *obj,
const GtkTextIter *iter,
GdkEventMotion *event);
gboolean (*extend_selection) (MooText *obj,
MooTextSelectionType type,
GtkTextIter *insert,
GtkTextIter *selection_bound);
void (*window_to_buffer_coords) (MooText *obj,
int window_x,
int window_y,
int *buffer_x,
int *buffer_y);
void (*get_iter_at_location) (MooText *obj,
GtkTextIter *iter,
int x,
int y);
gboolean (*get_selection_bounds) (MooText *obj,
GtkTextIter *sel_start,
GtkTextIter *sel_end);
void (*iter_order) (GtkTextIter *first,
GtkTextIter *second);
gboolean (*iter_in_range) (const GtkTextIter *iter,
const GtkTextIter *start,
const GtkTextIter *end);
void (*place_selection_end) (MooText *obj,
const GtkTextIter *where);
void (*select_range) (MooText *obj,
const GtkTextIter *start,
const GtkTextIter *end);
void (*scroll_selection_end_onscreen) (MooText *obj);
GdkWindow* (*get_window) (MooText *obj);
void (*get_visible_rect) (MooText *obj,
GdkRectangle *rect);
};
GType moo_text_get_type (void);
gboolean moo_text_button_press_event (GtkWidget *widget,
GdkEventButton *event);
gboolean moo_text_button_release_event (GtkWidget *widget,
GdkEventButton *event);
gboolean moo_text_motion_event (GtkWidget *widget,
GdkEventMotion *event);
G_END_DECLS
#endif /* MOOUI_MOOTEXT_H */

View File

@ -108,7 +108,7 @@ static guint signals[LAST_SIGNAL] = {0};
/* MOO_TYPE_WINDOW */
static gpointer moo_window_parent_class = NULL;
GType moo_window_get_type ()
GType moo_window_get_type (void)
{
static GType type = 0;
if (!type)

View File

@ -24,7 +24,7 @@
</ignoreparts>
<projectdirectory>.</projectdirectory>
<absoluteprojectpath>false</absoluteprojectpath>
<description></description>
<description/>
<secondaryLanguages>
<language>C</language>
</secondaryLanguages>
@ -39,7 +39,7 @@
<mainprogram>tests/mterm</mainprogram>
<directoryradio>executable</directoryradio>
<customdirectory>/</customdirectory>
<programargs></programargs>
<programargs/>
<terminal>false</terminal>
<autocompile>true</autocompile>
<envvars/>
@ -141,7 +141,7 @@
<abortonerror>true</abortonerror>
<numberofjobs>1</numberofjobs>
<dontact>false</dontact>
<makebin></makebin>
<makebin/>
<prio>0</prio>
</make>
</kdevautoproject>
@ -149,10 +149,10 @@
<general>
<dbgshell>libtool</dbgshell>
<programargs>--g-fatal-warnings bash</programargs>
<gdbpath></gdbpath>
<configGdbScript></configGdbScript>
<runShellScript></runShellScript>
<runGdbScript></runGdbScript>
<gdbpath/>
<configGdbScript/>
<runShellScript/>
<runGdbScript/>
<breakonloadinglibs>true</breakonloadinglibs>
<separatetty>false</separatetty>
<floatingtoolbar>true</floatingtoolbar>
@ -253,7 +253,7 @@
</codecompletion>
<references/>
<creategettersetter>
<prefixGet></prefixGet>
<prefixGet/>
<prefixSet>set</prefixSet>
<prefixVariable>m_,_</prefixVariable>
<parameterName>theValue</parameterName>