diff console/libgnt/gnttextview.c @ 15750:0eb7846f9e7e

Add a gntclipboard. You can select text in a textview with the mouse, and paste it in an entry with ctrl-v (or rebind GntEntry's clipboard-paste). If you use the s.so WM, pressing alt-shift-c ("toggle-clipboard") will toggle display of the clipboard contents in a possibly easy-to-copy-with-the-x-mouse window. This includes a plugin which interacts with the X selection, which is not built by default.
author Richard Nelson <wabz@pidgin.im>
date Fri, 02 Mar 2007 01:48:11 +0000
parents a09936753f2f
children e0027ebd2300
line wrap: on
line diff
--- a/console/libgnt/gnttextview.c	Thu Mar 01 21:52:57 2007 +0000
+++ b/console/libgnt/gnttextview.c	Fri Mar 02 01:48:11 2007 +0000
@@ -32,6 +32,10 @@
 
 static GntWidgetClass *parent_class = NULL;
 
+static gchar *select_start;
+static gchar *select_end;
+static gboolean double_click;
+
 static void
 gnt_text_view_draw(GntWidget *widget)
 {
@@ -54,9 +58,29 @@
 			GntTextSegment *seg = iter->data;
 			char *end = view->string->str + seg->end;
 			char back = *end;
+			chtype fl = seg->flags;
 			*end = '\0';
-			wattrset(widget->window, seg->flags);
-			wprintw(widget->window, "%s", (view->string->str + seg->start));
+			if (select_start < view->string->str + seg->start && select_end > view->string->str + seg->end) {
+				fl |= A_REVERSE;
+				wattrset(widget->window, fl);
+				wprintw(widget->window, "%s", (view->string->str + seg->start));
+			} else if (select_start && select_end &&
+				((select_start >= view->string->str + seg->start && select_start <= view->string->str + seg->end) ||
+				(select_end <= view->string->str + seg->end && select_start <= view->string->str + seg->start))) {
+				char *cur = view->string->str + seg->start;
+				while (*cur != '\0') {
+					if (cur >= select_start && cur <= select_end)
+						fl |= A_REVERSE;
+					else
+						fl = seg->flags;
+					wattrset(widget->window, fl);
+					waddch(widget->window, *cur);
+					cur++;
+				}
+			} else {
+				wattrset(widget->window, fl);
+				wprintw(widget->window, "%s", (view->string->str + seg->start));
+			}
 			*end = back;
 		}
 		wattroff(widget->window, A_UNDERLINE | A_BLINK | A_REVERSE);
@@ -160,6 +184,66 @@
 	g_string_free(view->string, TRUE);
 }
 
+static char *
+gnt_text_view_get_p(GntTextView *view, int x, int y)
+{
+	int i;
+	GntWidget *wid = GNT_WIDGET(view);
+	GntTextLine *line;
+	GList *lines;
+	GList *segs;
+	GntTextSegment *seg;
+
+	y = wid->priv.height - y;
+	if (g_list_length(view->list) < y) {
+		x = 0;
+		y = g_list_length(view->list);
+	}
+
+	lines = g_list_nth(view->list, y - 1);
+
+	line = lines->data;
+	for (i = y; line && !line->segments; i++)
+		line = g_list_nth_data(lines, i);
+	if (!line) /* no valid line */
+		return NULL;
+	segs = line->segments;
+	seg = (GntTextSegment *)segs->data;
+	i = 0;
+	return view->string->str + seg->start + x;
+}
+
+static GString *
+select_word_text(GntTextView *view, gchar *c)
+{
+	gchar *start = c;
+	gchar *end = c;
+	gchar *t;
+	while (t = g_utf8_prev_char(start)) {
+		if (!g_ascii_isspace(*t)) {
+			if (start == view->string->str)
+				break;
+			start = t;
+		} else
+			break;
+	}
+	while (t = g_utf8_next_char(end)) {
+		if (!g_ascii_isspace(*t))
+			end = t;
+		else
+			break;
+	}
+	select_start = start;
+	select_end = end;
+	return g_string_new_len(start, end - start + 1);
+}
+
+static gboolean too_slow(gpointer n)
+{
+	double_click = FALSE;
+	return FALSE;
+}
+
 static gboolean
 gnt_text_view_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
 {
@@ -167,6 +251,36 @@
 		gnt_text_view_scroll(GNT_TEXT_VIEW(widget), -1);
 	} else if (event == GNT_MOUSE_SCROLL_DOWN) {
 		gnt_text_view_scroll(GNT_TEXT_VIEW(widget), 1);
+	} else if (event == GNT_LEFT_MOUSE_DOWN) {
+		select_start = gnt_text_view_get_p(GNT_TEXT_VIEW(widget), x - widget->priv.x, y - widget->priv.y);
+		g_timeout_add(500, too_slow, NULL);
+	} else if (event == GNT_MOUSE_UP) {
+		if (select_start) {
+			GString *clip;
+			select_end = gnt_text_view_get_p(GNT_TEXT_VIEW(widget), x - widget->priv.x, y - widget->priv.y);
+			if (select_end < select_start) {
+				gchar *t = select_start;
+				select_start = select_end;
+				select_end = t;
+			}
+			if (select_start == select_end) {
+				if (double_click) {
+					clip = select_word_text(GNT_TEXT_VIEW(widget), select_start);
+					double_click = FALSE;
+				} else {
+					double_click = TRUE;
+					select_start = 0;
+					select_end = 0;
+					gnt_widget_draw(widget);
+					return;
+				}
+			} else {
+				clip = g_string_new_len(select_start, select_end - select_start + 1);
+			}
+			gnt_widget_draw(widget);
+			gnt_set_clipboard_string(clip->str);
+			g_string_free(clip, TRUE);
+		}
 	} else
 		return FALSE;
 	return TRUE;