Skip to content

Implement text selection #9

Description

@dimitrovs

/* Text Selection Mode for hterm - Add to x.c */

/* Add these variables near the top with other static variables (around line 250) */
static int text_selection_mode = 0;
static int selection_start_x = 0;
static int selection_start_y = 0;
static int selection_anchor_x = 0;
static int selection_anchor_y = 0;
static struct timespec last_click_time;
static int click_count = 0;

/* Modify the bpress function to handle text selection mode entry */
void
bpress(XEvent *e)
{
int btn = e->xbutton.button;
struct timespec now;
int snap;

if (1 <= btn && btn <= 11)
	buttons |= 1 << (btn-1);

/* Handle text selection mode for left click (Button1) */
if (btn == Button1 && !IS_SET(MODE_MOUSE)) {
	clock_gettime(CLOCK_MONOTONIC, &now);
	
	/* Check if we're in text selection mode */
	if (text_selection_mode) {
		/* Exit text selection mode on second click */
		text_selection_mode = 0;
		/* Keep the selection */
		setsel(getsel(), e->xbutton.time);
		return;
	} else {
		/* Enter text selection mode on first click */
		text_selection_mode = 1;
		
		/* Clear any existing selection */
		selclear();
		
		/* Store the starting position */
		selection_start_x = evcol(e);
		selection_start_y = evrow(e);
		selection_anchor_x = selection_start_x;
		selection_anchor_y = selection_start_y;
		
		/* Start selection at current position */
		selstart(selection_start_x, selection_start_y, 0);
		
		/* Store click time for potential double/triple click detection */
		last_click_time = now;
		
		return;
	}
}

/* Original mouse handling code for other cases */
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
	mousereport(e);
	return;
}

if (mouseaction(e, 0))
	return;

/* Original Button1 handling when not in text selection mode */
if (btn == Button1 && !text_selection_mode) {
	clock_gettime(CLOCK_MONOTONIC, &now);
	if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) {
		snap = SNAP_LINE;
	} else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) {
		snap = SNAP_WORD;
	} else {
		snap = 0;
	}
	xsel.tclick2 = xsel.tclick1;
	xsel.tclick1 = now;

	selstart(evcol(e), evrow(e), snap);
}

}

/* Modify the bmotion function to handle text selection during movement */
void
bmotion(XEvent e)
{
/
Handle text selection mode */
if (text_selection_mode) {
int current_x = evcol(e);
int current_y = evrow(e);

	/* Extend selection to current mouse position */
	selextend(current_x, current_y, SEL_REGULAR, 0);
	
	/* Trigger redraw to show selection */
	draw();
	return;
}

/* Original trackpad motion handling */
if (IS_SET(MODE_TRACKPAD)) {
	trackpadmotion(e);
	return;
}

/* Original mouse motion handling */
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
	mousereport(e);
	return;
}

mousesel(e, 0);

}

/* Add a helper function to check if we're in text selection mode */
int
is_text_selection_mode(void)
{
return text_selection_mode;
}

/* Modify the focus function to cancel text selection mode on focus loss */
void
focus(XEvent *ev)
{
XFocusChangeEvent *e = &ev->xfocus;

if (e->mode == NotifyGrab)
	return;

if (ev->type == FocusIn) {
	if (xw.ime.xic)
		XSetICFocus(xw.ime.xic);
	win.mode |= MODE_FOCUSED;
	xseturgency(0);
	if (IS_SET(MODE_FOCUS))
		ttywrite("\033[I", 3, 0);
	if (trackpadRemap) {
		win.mode |= MODE_TRACKPAD;
		trackpad_reset_pos = 1;
		XGrabPointer(xw.dpy, xw.win, False, PointerMotionMask,
		             GrabModeAsync, GrabModeAsync, None, blank_cursor, CurrentTime);
	}
} else {
	if (xw.ime.xic)
		XUnsetICFocus(xw.ime.xic);
	win.mode &= ~MODE_FOCUSED;
	if (IS_SET(MODE_FOCUS))
		ttywrite("\033[O", 3, 0);
	if (trackpadRemap) {
		win.mode &= ~MODE_TRACKPAD;
		trackpad_dx = 0;
		trackpad_dy = 0;
		XUngrabPointer(xw.dpy, CurrentTime);
		XDefineCursor(xw.dpy, xw.win, normal_cursor);
	}
	/* Cancel text selection mode on focus loss */
	text_selection_mode = 0;
}

}

/* Optional: Add keyboard shortcuts to cancel text selection mode */
void
kpress(XEvent *ev)
{
XKeyEvent *e = &ev->xkey;
KeySym ksym = NoSymbol;
char buf[64], *customkey;
int len;
Rune c;
Status status;
Shortcut *bp;

/* Cancel text selection mode on Escape key */
if (text_selection_mode) {
	if (xw.ime.xic) {
		len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
		if (status == XBufferOverflow)
			return;
	} else {
		len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
	}
	
	if (ksym == XK_Escape) {
		text_selection_mode = 0;
		selclear();
		return;
	}
}

/* Original kpress code continues... */
if (IS_SET(MODE_KBDLOCK))
	return;

if (xw.ime.xic) {
	len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
	if (status == XBufferOverflow)
		return;
} else {
	len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
}

/* 1. shortcuts */
for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
	if (ksym == bp->keysym && match(bp->mod, e->state)) {
		bp->func(&(bp->arg));
		return;
	}
}

/* 2. custom keys from config.h */
if ((customkey = kmap(ksym, e->state))) {
	ttywrite(customkey, strlen(customkey), 1);
	return;
}

/* 3. composed string from input method */
if (len == 0)
	return;
if (len == 1 && e->state & Mod1Mask) {
	if (IS_SET(MODE_8BIT)) {
		if (*buf < 0177) {
			c = *buf | 0x80;
			len = utf8encode(c, buf);
		}
	} else {
		buf[1] = buf[0];
		buf[0] = '\033';
		len = 2;
	}
}
ttywrite(buf, len, 1);

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions