#include <xterm.h>
#include <stdio.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
#include <X11/Xmu/StdSel.h>
#include <xutf8.h>
#include <fontutils.h>
#include <data.h>
#include <error.h>
#include <menu.h>
#include <xcharmouse.h>
#include <charclass.h>
#include <xstrings.h>
#if OPT_SELECT_REGEX
#ifdef HAVE_PCREPOSIX_H
#include <pcreposix.h>
#else
#include <sys/types.h>
#include <regex.h>
#endif
#endif
#if OPT_WIDE_CHARS
#include <ctype.h>
#include <wcwidth.h>
#else
#define CharacterClass(value) \
charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
#endif
#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
#define KeyModifiers (event->xbutton.state & OurModifiers)
#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
+ (((x) & Mod1Mask) ? 2 : 0))
#define Coordinate(s,c) ((c)->row * MaxCols(s) + (c)->col)
static const CELL zeroCELL =
{0, 0};
#if OPT_DEC_LOCATOR
static Bool SendLocatorPosition(XtermWidget xw, XEvent * event);
static void CheckLocatorPosition(XtermWidget xw, XEvent * event);
#endif
#if OPT_READLINE
static Time lastButtonDownTime = 0;
static int ExtendingSelection = 0;
static Time lastButton3UpTime = 0;
static Time lastButton3DoubleDownTime = 0;
static CELL lastButton3;
#endif
static Char *SaveText(TScreen * screen, int row, int scol, int ecol,
Char * lp, int *eol);
static int Length(TScreen * screen, int row, int scol, int ecol);
static void ComputeSelect(XtermWidget xw, CELL * startc, CELL * endc, Bool extend);
static void EditorButton(XtermWidget xw, XButtonEvent * event);
static void EndExtend(XtermWidget w, XEvent * event, String * params, Cardinal
num_params, Bool use_cursor_loc);
static void ExtendExtend(XtermWidget xw, const CELL * cell);
static void PointToCELL(TScreen * screen, int y, int x, CELL * cell);
static void ReHiliteText(XtermWidget xw, CELL * first, CELL * last);
static void SaltTextAway(XtermWidget xw, CELL * cellc, CELL * cell,
String * params, Cardinal num_params);
static void SelectSet(XtermWidget xw, XEvent * event, String * params, Cardinal num_params);
static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
static void StartSelect(XtermWidget xw, const CELL * cell);
static void TrackDown(XtermWidget xw, XButtonEvent * event);
static void TrackText(XtermWidget xw, const CELL * first, const CELL * last);
static void _OwnSelection(XtermWidget xw, String * selections, Cardinal count);
static void do_select_end(XtermWidget xw, XEvent * event, String * params,
Cardinal *num_params, Bool use_cursor_loc);
Bool
SendMousePosition(XtermWidget xw, XEvent * event)
{
TScreen *screen = TScreenOf(xw);
if (screen->send_mouse_pos == MOUSE_OFF)
return False;
#if OPT_DEC_LOCATOR
if (screen->send_mouse_pos == DEC_LOCATOR) {
return (SendLocatorPosition(xw, event));
}
#endif
if ((screen->send_mouse_pos != BTN_EVENT_MOUSE)
&& (screen->send_mouse_pos != ANY_EVENT_MOUSE)
&& event->type != ButtonPress
&& event->type != ButtonRelease)
return False;
switch (screen->send_mouse_pos) {
case X10_MOUSE:
if (KeyModifiers == 0) {
if (event->type == ButtonPress)
EditorButton(xw, (XButtonEvent *) event);
return True;
}
return False;
case VT200_HIGHLIGHT_MOUSE:
if (event->type == ButtonPress &&
KeyModifiers == 0 &&
event->xbutton.button == Button1) {
TrackDown(xw, (XButtonEvent *) event);
return True;
}
if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
EditorButton(xw, (XButtonEvent *) event);
return True;
}
return False;
case VT200_MOUSE:
case BTN_EVENT_MOUSE:
case ANY_EVENT_MOUSE:
if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
if (event->type == MotionNotify) {
((XButtonEvent *) event)->button = 0;
}
EditorButton(xw, (XButtonEvent *) event);
return True;
}
return False;
default:
return False;
}
}
#if OPT_DEC_LOCATOR
#define LocatorCoords( row, col, x, y, oor ) \
if( screen->locator_pixels ) { \
(oor)=False; (row) = (y)+1; (col) = (x)+1; \
\
if ((row) < 1) (row) = 1,(oor)=True; \
else if ((row) > screen->border*2+Height(screen)) \
(row) = screen->border*2+Height(screen),(oor)=True; \
if ((col) < 1) (col) = 1,(oor)=True; \
else if ((col) > OriginX(screen)*2+Width(screen)) \
(col) = OriginX(screen)*2+Width(screen),(oor)=True; \
} else { \
(oor)=False; \
\
(row) = ((y) - screen->border) / FontHeight(screen); \
(col) = ((x) - OriginX(screen)) / FontWidth(screen); \
\
if ((row) < 0) (row) = 0,(oor)=True; \
else if ((row) > screen->max_row) \
(row) = screen->max_row,(oor)=True; \
if ((col) < 0) (col) = 0,(oor)=True; \
else if ((col) > screen->max_col) \
(col) = screen->max_col,(oor)=True; \
(row)++; (col)++; \
}
static Bool
SendLocatorPosition(XtermWidget xw, XEvent * event)
{
ANSI reply;
TScreen *screen = TScreenOf(xw);
int row, col;
Bool oor;
int button;
unsigned state;
if ((event->type != ButtonPress &&
event->type != ButtonRelease &&
!screen->loc_filter) ||
(KeyModifiers != 0 && KeyModifiers != ControlMask))
return (False);
if ((event->type == ButtonPress &&
!(screen->locator_events & LOC_BTNS_DN)) ||
(event->type == ButtonRelease &&
!(screen->locator_events & LOC_BTNS_UP)))
return (True);
if (event->type == MotionNotify) {
CheckLocatorPosition(xw, event);
return (True);
}
button = (int) event->xbutton.button - 1;
LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
memset(&reply, 0, sizeof(reply));
reply.a_type = ANSI_CSI;
if (oor) {
reply.a_nparam = 1;
reply.a_param[0] = 0;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
return (True);
}
reply.a_nparam = 4;
switch (event->type) {
case ButtonPress:
reply.a_param[0] = (ParmType) (2 + (button << 1));
break;
case ButtonRelease:
reply.a_param[0] = (ParmType) (3 + (button << 1));
break;
default:
return (True);
}
state = (event->xbutton.state
& (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
state ^= 1 << button;
state = (state & ~(4 | 1)) | ((state & 1) ? 4 : 0) | ((state & 4) ? 1 : 0);
reply.a_param[1] = (ParmType) state;
reply.a_param[2] = (ParmType) row;
reply.a_param[3] = (ParmType) col;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
if (screen->loc_filter) {
screen->send_mouse_pos = MOUSE_OFF;
screen->loc_filter = False;
screen->locator_events = 0;
MotionOff(screen, xw);
}
return (True);
}
#define ButtonState(state, mask) \
{ (state) = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8); \
\
(state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0); \
}
void
GetLocatorPosition(XtermWidget xw)
{
ANSI reply;
TScreen *screen = TScreenOf(xw);
Window root, child;
int rx, ry, x, y;
unsigned int mask;
int row = 0, col = 0;
Bool oor = False;
Bool ret = False;
int state;
if (screen->loc_filter) {
screen->send_mouse_pos = MOUSE_OFF;
screen->loc_filter = False;
screen->locator_events = 0;
MotionOff(screen, xw);
}
memset(&reply, 0, sizeof(reply));
reply.a_type = ANSI_CSI;
if (screen->send_mouse_pos == DEC_LOCATOR) {
ret = XQueryPointer(screen->display, VWindow(screen), &root,
&child, &rx, &ry, &x, &y, &mask);
if (ret) {
LocatorCoords(row, col, x, y, oor);
}
}
if (ret == False || oor) {
reply.a_nparam = 1;
reply.a_param[0] = 0;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
return;
}
ButtonState(state, mask);
reply.a_nparam = 4;
reply.a_param[0] = 1;
reply.a_param[1] = (ParmType) state;
reply.a_param[2] = (ParmType) row;
reply.a_param[3] = (ParmType) col;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
}
void
InitLocatorFilter(XtermWidget xw)
{
ANSI reply;
TScreen *screen = TScreenOf(xw);
Window root, child;
int rx, ry, x, y;
unsigned int mask;
int row = 0, col = 0;
Bool oor = 0;
Bool ret;
int state;
ret = XQueryPointer(screen->display, VWindow(screen),
&root, &child, &rx, &ry, &x, &y, &mask);
if (ret) {
LocatorCoords(row, col, x, y, oor);
}
if (ret == False || oor) {
if (screen->loc_filter_top != LOC_FILTER_POS ||
screen->loc_filter_left != LOC_FILTER_POS ||
screen->loc_filter_bottom != LOC_FILTER_POS ||
screen->loc_filter_right != LOC_FILTER_POS) {
memset(&reply, 0, sizeof(reply));
reply.a_type = ANSI_CSI;
reply.a_nparam = 1;
reply.a_param[0] = 0;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
} else {
screen->loc_filter = True;
MotionOn(screen, xw);
}
return;
}
if (screen->locator_pixels) {
rx = OriginX(screen) * 2 + Width(screen);
ry = screen->border * 2 + Height(screen);
} else {
rx = screen->max_col;
ry = screen->max_row;
}
#define Adjust( coord, def, max ) \
if( (coord) == LOC_FILTER_POS ) (coord) = (def); \
else if ((coord) < 1) (coord) = 1; \
else if ((coord) > (max)) (coord) = (max)
Adjust(screen->loc_filter_top, row, ry);
Adjust(screen->loc_filter_left, col, rx);
Adjust(screen->loc_filter_bottom, row, ry);
Adjust(screen->loc_filter_right, col, rx);
if (screen->loc_filter_top > screen->loc_filter_bottom) {
ry = screen->loc_filter_top;
screen->loc_filter_top = screen->loc_filter_bottom;
screen->loc_filter_bottom = ry;
}
if (screen->loc_filter_left > screen->loc_filter_right) {
rx = screen->loc_filter_left;
screen->loc_filter_left = screen->loc_filter_right;
screen->loc_filter_right = rx;
}
if ((col < screen->loc_filter_left) ||
(col > screen->loc_filter_right) ||
(row < screen->loc_filter_top) ||
(row > screen->loc_filter_bottom)) {
ButtonState(state, mask);
memset(&reply, 0, sizeof(reply));
reply.a_type = ANSI_CSI;
reply.a_nparam = 4;
reply.a_param[0] = 10;
reply.a_param[1] = (ParmType) state;
reply.a_param[2] = (ParmType) row;
reply.a_param[3] = (ParmType) col;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
return;
}
screen->loc_filter = True;
MotionOn(screen, xw);
}
static void
CheckLocatorPosition(XtermWidget xw, XEvent * event)
{
ANSI reply;
TScreen *screen = TScreenOf(xw);
int row, col;
Bool oor;
int state;
LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
(col < screen->loc_filter_left) ||
(col > screen->loc_filter_right) ||
(row < screen->loc_filter_top) ||
(row > screen->loc_filter_bottom)) {
screen->loc_filter = False;
MotionOff(screen, xw);
memset(&reply, 0, sizeof(reply));
reply.a_type = ANSI_CSI;
if (oor) {
reply.a_nparam = 1;
reply.a_param[0] = 0;
} else {
ButtonState(state, event->xbutton.state);
reply.a_nparam = 4;
reply.a_param[0] = 10;
reply.a_param[1] = (ParmType) state;
reply.a_param[2] = (ParmType) row;
reply.a_param[3] = (ParmType) col;
}
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(xw, &reply);
if (screen->locator_reset) {
MotionOff(screen, xw);
screen->send_mouse_pos = MOUSE_OFF;
}
}
}
#endif
#if OPT_READLINE
static int
isClick1_clean(TScreen * screen, XEvent * event)
{
int delta;
if (!(event->type == ButtonPress || event->type == ButtonRelease)
|| (KeyModifiers & ShiftMask)
|| (screen->send_mouse_pos != MOUSE_OFF)
||ExtendingSelection)
return 0;
if (event->type != ButtonRelease)
return 0;
if (lastButtonDownTime == (Time) 0) {
delta = screen->multiClickTime + 1;
} else if (event->xbutton.time > lastButtonDownTime) {
delta = (int) (event->xbutton.time - lastButtonDownTime);
} else {
delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
}
return delta <= screen->multiClickTime;
}
static int
isDoubleClick3(TScreen * screen, XEvent * event)
{
int delta;
if (event->type != ButtonRelease
|| (KeyModifiers & ShiftMask)
|| event->xbutton.button != Button3) {
lastButton3UpTime = 0;
return 0;
}
if (lastButton3DoubleDownTime == (Time) 0) {
delta = screen->multiClickTime + 1;
} else if (event->xbutton.time > lastButton3DoubleDownTime) {
delta = (int) (event->xbutton.time - lastButton3DoubleDownTime);
} else {
delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->xbutton.time);
}
if (delta <= screen->multiClickTime) {
CELL cell;
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
if (isSameCELL(&cell, &lastButton3)) {
lastButton3DoubleDownTime = 0;
return 1;
}
}
lastButton3UpTime = event->xbutton.time;
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
return 0;
}
static int
CheckSecondPress3(TScreen * screen, XEvent * event)
{
int delta;
if (event->type != ButtonPress
|| (KeyModifiers & ShiftMask)
|| event->xbutton.button != Button3) {
lastButton3DoubleDownTime = 0;
return 0;
}
if (lastButton3UpTime == (Time) 0) {
delta = screen->multiClickTime + 1;
} else if (event->xbutton.time > lastButton3UpTime) {
delta = (int) (event->xbutton.time - lastButton3UpTime);
} else {
delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
}
if (delta <= screen->multiClickTime) {
CELL cell;
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
if (isSameCELL(&cell, &lastButton3)) {
lastButton3DoubleDownTime = event->xbutton.time;
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
return 1;
}
lastButton3UpTime = 0;
}
lastButton3DoubleDownTime = 0;
return 0;
}
static int
rowOnCurrentLine(TScreen * screen,
int line,
int *deltap)
{
int l1, l2;
*deltap = 0;
if (line == screen->cur_row)
return 1;
if (line < screen->cur_row)
l1 = line, l2 = screen->cur_row;
else
l2 = line, l1 = screen->cur_row;
l1--;
while (++l1 < l2)
if (!ScrnTstWrapped(screen, l1))
return 0;
*deltap = line - screen->cur_row;
return 1;
}
static int
eventRow(TScreen * screen, XEvent * event)
{
return (event->xbutton.y - screen->border) / FontHeight(screen);
}
static int
eventColBetween(TScreen * screen, XEvent * event)
{
return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
/ FontWidth(screen));
}
static int
ReadLineMovePoint(TScreen * screen, int col, int ldelta)
{
Char line[6];
unsigned count = 0;
col += ldelta * MaxCols(screen) - screen->cur_col;
if (col == 0)
return 0;
if (screen->control_eight_bits) {
line[count++] = ANSI_CSI;
} else {
line[count++] = ANSI_ESC;
line[count++] = '[';
}
line[count++] = CharOf(col > 0 ? 'C' : 'D');
if (col < 0)
col = -col;
while (col--)
v_write(screen->respond, line, 3);
return 1;
}
static int
ReadLineDelete(TScreen * screen, CELL * cell1, CELL * cell2)
{
int del;
del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
if (del <= 0)
return 0;
while (del--)
v_write(screen->respond, (Char *) "\177", 1);
return 1;
}
#endif
void
DiredButton(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
Char Line[6];
unsigned line, col;
if ((event->type == ButtonPress || event->type == ButtonRelease)
&& (event->xbutton.y >= screen->border)
&& (event->xbutton.x >= OriginX(screen))) {
line = ((unsigned) (event->xbutton.y - screen->border)
/ FontHeight(screen));
col = ((unsigned) (event->xbutton.x - OriginX(screen))
/ FontWidth(screen));
Line[0] = CONTROL('X');
Line[1] = ANSI_ESC;
Line[2] = 'G';
Line[3] = CharOf(' ' + col);
Line[4] = CharOf(' ' + line);
v_write(screen->respond, Line, 5);
}
}
}
#if OPT_READLINE
void
ReadLineButton(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
Char Line[6];
int line, col, ldelta = 0;
if (!(event->type == ButtonPress || event->type == ButtonRelease)
|| (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
goto finish;
if (event->type == ButtonRelease) {
int delta;
if (lastButtonDownTime == (Time) 0) {
delta = screen->multiClickTime + 1;
} else if (event->xbutton.time > lastButtonDownTime) {
delta = (int) (event->xbutton.time - lastButtonDownTime);
} else {
delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
}
if (delta > screen->multiClickTime)
goto finish;
}
line = (event->xbutton.y - screen->border) / FontHeight(screen);
if (line != screen->cur_row) {
int l1, l2;
if (line < screen->cur_row)
l1 = line, l2 = screen->cur_row;
else
l2 = line, l1 = screen->cur_row;
l1--;
while (++l1 < l2)
if (!ScrnTstWrapped(screen, l1))
goto finish;
ldelta = line - screen->cur_row;
}
col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
/ 2)
/ FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
if (col == 0)
goto finish;
Line[0] = ANSI_ESC;
Line[1] = 'O';
Line[2] = CharOf(col > 0 ? 'C' : 'D');
if (col < 0)
col = -col;
while (col--)
v_write(screen->respond, Line, 3);
finish:
if (event->type == ButtonRelease)
do_select_end(xw, event, params, num_params, False);
}
}
#endif
void
ViButton(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
int pty = screen->respond;
Char Line[6];
int line;
if (event->type == ButtonPress || event->type == ButtonRelease) {
line = screen->cur_row -
((event->xbutton.y - screen->border) / FontHeight(screen));
if (line != 0) {
Line[0] = ANSI_ESC;
v_write(pty, Line, 1);
if (line < 0) {
line = -line;
Line[0] = CONTROL('n');
} else {
Line[0] = CONTROL('p');
}
while (--line >= 0)
v_write(pty, Line, 1);
}
}
}
}
void
HandleSelectExtend(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
CELL cell;
screen->selection_time = event->xmotion.time;
switch (screen->eventMode) {
case LEFTEXTENSION:
case RIGHTEXTENSION:
PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
ExtendExtend(xw, &cell);
break;
case NORMAL:
if (screen->send_mouse_pos == BTN_EVENT_MOUSE
|| screen->send_mouse_pos == ANY_EVENT_MOUSE) {
(void) SendMousePosition(xw, event);
}
break;
}
}
}
void
HandleKeyboardSelectExtend(Widget w,
XEvent * event GCC_UNUSED,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
ExtendExtend(xw, &screen->cursorp);
}
}
static void
do_select_end(XtermWidget xw,
XEvent * event,
String * params,
Cardinal *num_params,
Bool use_cursor_loc)
{
#if OPT_READLINE
int ldelta1, ldelta2;
#endif
TScreen *screen = TScreenOf(xw);
screen->selection_time = event->xbutton.time;
switch (screen->eventMode) {
case NORMAL:
(void) SendMousePosition(xw, event);
break;
case LEFTEXTENSION:
case RIGHTEXTENSION:
EndExtend(xw, event, params, *num_params, use_cursor_loc);
#if OPT_READLINE
if (isClick1_clean(screen, event)
&& SCREEN_FLAG(screen, click1_moves)
&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
}
if (isDoubleClick3(screen, event)
&& SCREEN_FLAG(screen, dclick3_deletes)
&& rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
&& rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
}
#endif
break;
}
}
void
HandleSelectEnd(Widget w,
XEvent * event,
String * params,
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
do_select_end(xw, event, params, num_params, False);
}
}
void
HandleKeyboardSelectEnd(Widget w,
XEvent * event,
String * params,
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
do_select_end(xw, event, params, num_params, True);
}
}
struct _SelectionList {
String *params;
Cardinal count;
Atom *targets;
Time time;
};
static unsigned
DECtoASCII(unsigned ch)
{
if (xtermIsDecGraphic(ch)) {
ch = CharOf("###########+++++##-##++++|######"[ch]);
}
return ch;
}
#if OPT_WIDE_CHARS
static Char *
UTF8toLatin1(Char * s, unsigned len, unsigned long *result)
{
static Char *buffer;
static size_t used;
Char *q;
if (used == 0) {
buffer = (Char *) XtMalloc(1 + (used = len));
} else if (len > used) {
buffer = (Char *) XtRealloc((char *) buffer, 1 + (used = len));
}
if (buffer != 0) {
PtyData data;
q = buffer;
fakePtyData(&data, s, s + len);
while (decodeUtf8(&data)) {
IChar value = skipPtyData(&data);
if (value == UCS_REPL) {
*q++ = '#';
} else if (value < 256) {
*q++ = CharOf(value);
} else {
unsigned eqv = ucs2dec(value);
if (xtermIsDecGraphic(eqv)) {
*q++ = (Char) DECtoASCII(eqv);
} else {
eqv = AsciiEquivs(value);
if (eqv == value)
eqv = '#';
*q++ = (Char) eqv;
if (iswide((wchar_t) value))
*q++ = ' ';
}
}
}
*q = 0;
*result = (unsigned long) (q - buffer);
} else {
*result = 0;
}
return buffer;
}
#endif
static Atom *
_SelectionTargets(Widget w)
{
static Atom *eightBitSelectionTargets = NULL;
TScreen *screen;
int n;
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0)
return NULL;
screen = TScreenOf(xw);
#if OPT_WIDE_CHARS
if (screen->wide_chars) {
static Atom *utf8SelectionTargets = NULL;
if (utf8SelectionTargets == NULL) {
utf8SelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom));
if (utf8SelectionTargets == NULL) {
TRACE(("Couldn't allocate utf8SelectionTargets\n"));
return NULL;
}
n = 0;
utf8SelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w));
#ifdef X_HAVE_UTF8_STRING
if (screen->i18nSelections) {
utf8SelectionTargets[n++] = XA_TEXT(XtDisplay(w));
utf8SelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
}
#endif
utf8SelectionTargets[n++] = XA_STRING;
utf8SelectionTargets[n] = None;
}
return utf8SelectionTargets;
}
#endif
if (eightBitSelectionTargets == NULL) {
eightBitSelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom));
if (eightBitSelectionTargets == NULL) {
TRACE(("Couldn't allocate eightBitSelectionTargets\n"));
return NULL;
}
n = 0;
#ifdef X_HAVE_UTF8_STRING
eightBitSelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w));
#endif
if (screen->i18nSelections) {
eightBitSelectionTargets[n++] = XA_TEXT(XtDisplay(w));
eightBitSelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
}
eightBitSelectionTargets[n++] = XA_STRING;
eightBitSelectionTargets[n] = None;
}
return eightBitSelectionTargets;
}
#define isSELECT(value) (!strcmp(value, "SELECT"))
static void
UnmapSelections(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
Cardinal n;
if (screen->mappedSelect) {
for (n = 0; screen->mappedSelect[n] != 0; ++n)
free(screen->mappedSelect[n]);
free(screen->mappedSelect);
screen->mappedSelect = 0;
}
}
static String *
MapSelections(XtermWidget xw, String * params, Cardinal num_params)
{
String *result = params;
if (num_params > 0) {
Cardinal j;
Boolean map = False;
for (j = 0; j < num_params; ++j) {
TRACE(("param[%d]:%s\n", j, params[j]));
if (isSELECT(params[j])) {
map = True;
break;
}
}
if (map) {
TScreen *screen = TScreenOf(xw);
const char *mapTo = (screen->selectToClipboard
? "CLIPBOARD"
: "PRIMARY");
UnmapSelections(xw);
if ((result = TypeMallocN(String, num_params + 1)) != 0) {
result[num_params] = 0;
for (j = 0; j < num_params; ++j) {
result[j] = x_strdup((isSELECT(params[j])
? mapTo
: params[j]));
if (result[j] == 0) {
UnmapSelections(xw);
result = 0;
break;
}
}
screen->mappedSelect = result;
}
}
}
return result;
}
static int
CutBuffer(unsigned code)
{
int cutbuffer;
switch (code) {
case XA_CUT_BUFFER0:
cutbuffer = 0;
break;
case XA_CUT_BUFFER1:
cutbuffer = 1;
break;
case XA_CUT_BUFFER2:
cutbuffer = 2;
break;
case XA_CUT_BUFFER3:
cutbuffer = 3;
break;
case XA_CUT_BUFFER4:
cutbuffer = 4;
break;
case XA_CUT_BUFFER5:
cutbuffer = 5;
break;
case XA_CUT_BUFFER6:
cutbuffer = 6;
break;
case XA_CUT_BUFFER7:
cutbuffer = 7;
break;
default:
cutbuffer = -1;
break;
}
return cutbuffer;
}
#if OPT_PASTE64
static void
FinishPaste64(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
if (screen->base64_paste) {
screen->base64_paste = 0;
unparseputc1(xw, screen->base64_final);
unparse_end(xw);
}
}
#endif
#if !OPT_PASTE64
static
#endif
void
xtermGetSelection(Widget w,
Time ev_time,
String * params,
Cardinal num_params,
Atom * targets)
{
Atom selection;
int cutbuffer;
Atom target;
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0)
return;
TRACE(("xtermGetSelection\n"));
params = MapSelections(xw, params, num_params);
XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
cutbuffer = CutBuffer(selection);
TRACE(("Cutbuffer: %d, target: %lu\n", cutbuffer,
targets ? (unsigned long) targets[0] : 0));
if (cutbuffer >= 0) {
int inbytes;
unsigned long nbytes;
int fmt8 = 8;
Atom type = XA_STRING;
char *line;
line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
nbytes = (unsigned long) inbytes;
if (nbytes > 0)
SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
&nbytes, &fmt8);
else if (num_params > 1) {
xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
}
#if OPT_PASTE64
else {
FinishPaste64(xw);
}
#endif
return;
} else {
struct _SelectionList *list;
if (targets == NULL || targets[0] == None) {
targets = _SelectionTargets(w);
}
if (targets != 0) {
target = targets[0];
if (targets[1] == None) {
params++;
num_params--;
targets = _SelectionTargets(w);
} else {
targets = &(targets[1]);
}
if (num_params) {
list = XtNew(struct _SelectionList);
if (list != 0) {
list->params = params;
list->count = num_params;
list->targets = targets;
list->time = ev_time;
}
} else {
list = NULL;
}
XtGetSelectionValue(w, selection,
target,
SelectionReceived,
(XtPointer) list, ev_time);
}
}
}
#if OPT_TRACE && OPT_WIDE_CHARS
static void
GettingSelection(Display * dpy, Atom type, Char * line, unsigned long len)
{
Char *cp;
char *name;
name = XGetAtomName(dpy, type);
TRACE(("Getting %s (%ld)\n", name, (long int) type));
for (cp = line; cp < line + len; cp++) {
TRACE(("[%d:%lu]", cp + 1 - line, len));
if (isprint(*cp)) {
TRACE(("%c\n", *cp));
} else {
TRACE(("\\x%02x\n", *cp));
}
}
}
#else
#define GettingSelection(dpy,type,line,len)
#endif
#ifdef VMS
# define tty_vwrite(pty,lag,l) tt_write(lag,l)
#else
# define tty_vwrite(pty,lag,l) v_write(pty,lag,l)
#endif
#if OPT_PASTE64
static const char base64_code[] = "\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789+/";
static void
base64_flush(TScreen * screen)
{
Char x;
switch (screen->base64_count) {
case 0:
break;
case 2:
x = CharOf(base64_code[screen->base64_accu << 4]);
tty_vwrite(screen->respond, &x, 1);
break;
case 4:
x = CharOf(base64_code[screen->base64_accu << 2]);
tty_vwrite(screen->respond, &x, 1);
break;
}
if (screen->base64_pad & 3)
tty_vwrite(screen->respond,
(Char *) "===",
(unsigned) (4 - (screen->base64_pad & 3)));
screen->base64_count = 0;
screen->base64_accu = 0;
screen->base64_pad = 0;
}
#endif
static void
_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length)
{
#if OPT_PASTE64
if (screen->base64_paste) {
Char *p = lag;
Char buf[64];
unsigned x = 0;
while (length--) {
switch (screen->base64_count) {
case 0:
buf[x++] = CharOf(base64_code[*p >> 2]);
screen->base64_accu = (unsigned) (*p & 0x3);
screen->base64_count = 2;
++p;
break;
case 2:
buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
(*p >> 4)]);
screen->base64_accu = (unsigned) (*p & 0xF);
screen->base64_count = 4;
++p;
break;
case 4:
buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
(*p >> 6)]);
buf[x++] = CharOf(base64_code[*p & 0x3F]);
screen->base64_accu = 0;
screen->base64_count = 0;
++p;
break;
}
if (x >= 63) {
screen->base64_pad += x;
tty_vwrite(screen->respond, buf, x);
x = 0;
}
}
if (x != 0) {
screen->base64_pad += x;
tty_vwrite(screen->respond, buf, x);
}
} else
#endif
#if OPT_READLINE
if (SCREEN_FLAG(screen, paste_quotes)) {
while (length--) {
tty_vwrite(screen->respond, (Char *) "\026", 1);
tty_vwrite(screen->respond, lag++, 1);
}
} else
#endif
tty_vwrite(screen->respond, lag, length);
}
static void
_WriteSelectionData(TScreen * screen, Char * line, unsigned length)
{
Char *lag, *end;
#ifdef VMS
tt_pasting = True;
#endif
end = &line[length];
lag = line;
#if OPT_PASTE64
if (screen->base64_paste) {
_qWriteSelectionData(screen, lag, (unsigned) (end - lag));
base64_flush(screen);
} else
#endif
{
if (!SCREEN_FLAG(screen, paste_literal_nl)) {
Char *cp;
for (cp = line; cp != end; cp++) {
if (*cp == '\n') {
*cp = '\r';
_qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1));
lag = cp + 1;
}
}
}
if (lag != end) {
_qWriteSelectionData(screen, lag, (unsigned) (end - lag));
}
}
#ifdef VMS
tt_pasting = False;
tt_start_read();
#endif
}
#if OPT_READLINE
static void
_WriteKey(TScreen * screen, Char * in)
{
Char line[16];
unsigned count = 0;
unsigned length = strlen((char *) in);
if (screen->control_eight_bits) {
line[count++] = ANSI_CSI;
} else {
line[count++] = ANSI_ESC;
line[count++] = '[';
}
while (length--)
line[count++] = *in++;
line[count++] = '~';
tty_vwrite(screen->respond, line, count);
}
#endif
static void
SelectionReceived(Widget w,
XtPointer client_data,
Atom * selection GCC_UNUSED,
Atom * type,
XtPointer value,
unsigned long *length,
int *format)
{
char **text_list = NULL;
int text_list_count;
XTextProperty text_prop;
TScreen *screen;
Display *dpy;
#if OPT_TRACE && OPT_WIDE_CHARS
Char *line = (Char *) value;
#endif
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0)
return;
screen = TScreenOf(xw);
dpy = XtDisplay(w);
if (*type == 0
|| *length == 0
|| value == NULL)
goto fail;
text_prop.value = (unsigned char *) value;
text_prop.encoding = *type;
text_prop.format = *format;
text_prop.nitems = *length;
#if OPT_WIDE_CHARS
if (screen->wide_chars) {
if (*type == XA_UTF8_STRING(dpy) ||
*type == XA_STRING ||
*type == XA_COMPOUND_TEXT(dpy)) {
GettingSelection(dpy, *type, line, *length);
if (Xutf8TextPropertyToTextList(dpy, &text_prop,
&text_list,
&text_list_count) < 0) {
TRACE(("Conversion failed\n"));
text_list = NULL;
}
}
} else
#endif
{
if (*type == XA_UTF8_STRING(dpy) ||
*type == XA_STRING ||
*type == XA_COMPOUND_TEXT(dpy)) {
Status rc;
GettingSelection(dpy, *type, line, *length);
#if OPT_WIDE_CHARS
if (*type == XA_UTF8_STRING(dpy) &&
!(screen->wide_chars || screen->c1_printable)) {
rc = Xutf8TextPropertyToTextList(dpy, &text_prop,
&text_list, &text_list_count);
if (text_list != NULL && text_list_count != 0) {
int i;
Char *data;
char **new_text_list, *tmp;
unsigned long size, new_size;
new_size = 0;
for (i = 0; i < text_list_count; ++i) {
data = (Char *) text_list[i];
size = strlen(text_list[i]) + 1;
data = UTF8toLatin1(data, size, &size);
new_size += size + 1;
}
new_text_list =
(char **) XtMalloc(sizeof(char *) * (unsigned) text_list_count);
new_text_list[0] = tmp = XtMalloc(new_size);
for (i = 0; i < text_list_count; ++i) {
data = (Char *) text_list[i];
size = strlen(text_list[i]) + 1;
data = UTF8toLatin1(data, size, &size);
memcpy(tmp, data, size + 1);
new_text_list[i] = tmp;
tmp += size + 1;
}
XFreeStringList(text_list);
text_list = new_text_list;
}
} else
#endif
if (*type == XA_STRING && screen->brokenSelections) {
rc = XTextPropertyToStringList(&text_prop,
&text_list, &text_list_count);
} else {
rc = XmbTextPropertyToTextList(dpy, &text_prop,
&text_list,
&text_list_count);
}
if (rc < 0) {
TRACE(("Conversion failed\n"));
text_list = NULL;
}
}
}
if (text_list != NULL && text_list_count != 0) {
int i;
#if OPT_PASTE64
if (screen->base64_paste) {
;
} else
#endif
#if OPT_READLINE
if (SCREEN_FLAG(screen, paste_brackets)) {
_WriteKey(screen, (Char *) "200");
}
#endif
for (i = 0; i < text_list_count; i++) {
unsigned len = strlen(text_list[i]);
_WriteSelectionData(screen, (Char *) text_list[i], len);
}
#if OPT_PASTE64
if (screen->base64_paste) {
FinishPaste64(xw);
} else
#endif
#if OPT_READLINE
if (SCREEN_FLAG(screen, paste_brackets)) {
_WriteKey(screen, (Char *) "201");
}
#endif
XFreeStringList(text_list);
} else
goto fail;
XtFree((char *) client_data);
XtFree((char *) value);
return;
fail:
if (client_data != 0) {
struct _SelectionList *list = (struct _SelectionList *) client_data;
xtermGetSelection(w, list->time,
list->params, list->count, list->targets);
XtFree((char *) client_data);
#if OPT_PASTE64
} else {
FinishPaste64(xw);
#endif
}
return;
}
void
HandleInsertSelection(Widget w,
XEvent * event,
String * params,
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
if (!SendMousePosition(xw, event)) {
#if OPT_READLINE
int ldelta;
TScreen *screen = TScreenOf(xw);
if ((event->type == ButtonPress || event->type == ButtonRelease)
&& !(KeyModifiers & ShiftMask)
&& (screen->send_mouse_pos == MOUSE_OFF)
&& SCREEN_FLAG(screen, paste_moves)
&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
#endif
xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
}
}
}
static SelectUnit
EvalSelectUnit(XtermWidget xw,
Time buttonDownTime,
SelectUnit defaultUnit,
unsigned int button)
{
TScreen *screen = TScreenOf(xw);
SelectUnit result;
int delta;
if (button != screen->lastButton) {
delta = xw->screen.multiClickTime + 1;
} else if (screen->lastButtonUpTime == (Time) 0) {
delta = screen->multiClickTime + 1;
} else if (buttonDownTime > screen->lastButtonUpTime) {
delta = (int) (buttonDownTime - screen->lastButtonUpTime);
} else {
delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
}
if (delta > screen->multiClickTime) {
screen->numberOfClicks = 1;
result = defaultUnit;
} else {
result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
screen->numberOfClicks += 1;
}
TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
return result;
}
static void
do_select_start(XtermWidget xw,
XEvent * event,
CELL * cell)
{
TScreen *screen = TScreenOf(xw);
if (SendMousePosition(xw, event))
return;
screen->selectUnit = EvalSelectUnit(xw,
event->xbutton.time,
Select_CHAR,
event->xbutton.button);
screen->replyToEmacs = False;
#if OPT_READLINE
lastButtonDownTime = event->xbutton.time;
#endif
StartSelect(xw, cell);
}
void
HandleSelectStart(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
CELL cell;
screen->firstValidRow = 0;
screen->lastValidRow = screen->max_row;
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
#if OPT_READLINE
ExtendingSelection = 0;
#endif
do_select_start(xw, event, &cell);
}
}
void
HandleKeyboardSelectStart(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
do_select_start(xw, event, &screen->cursorp);
}
}
static void
TrackDown(XtermWidget xw, XButtonEvent * event)
{
TScreen *screen = TScreenOf(xw);
CELL cell;
screen->selectUnit = EvalSelectUnit(xw,
event->time,
Select_CHAR,
event->button);
if (screen->numberOfClicks > 1) {
PointToCELL(screen, event->y, event->x, &cell);
screen->replyToEmacs = True;
StartSelect(xw, &cell);
} else {
screen->waitingForTrackInfo = True;
EditorButton(xw, (XButtonEvent *) event);
}
}
#define boundsCheck(x) if (x < 0) \
x = 0; \
else if (x >= screen->max_row) \
x = screen->max_row
void
TrackMouse(XtermWidget xw,
int func,
CELL * start,
int firstrow,
int lastrow)
{
TScreen *screen = TScreenOf(xw);
if (screen->waitingForTrackInfo) {
screen->waitingForTrackInfo = False;
if (func != 0) {
CELL first = *start;
boundsCheck(first.row);
boundsCheck(firstrow);
boundsCheck(lastrow);
screen->firstValidRow = firstrow;
screen->lastValidRow = lastrow;
screen->replyToEmacs = True;
StartSelect(xw, &first);
}
}
}
static void
StartSelect(XtermWidget xw, const CELL * cell)
{
TScreen *screen = TScreenOf(xw);
TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
if (screen->cursor_state)
HideCursor();
if (screen->numberOfClicks == 1) {
screen->rawPos = *cell;
}
screen->saveStartR = screen->startExt = screen->rawPos;
screen->saveEndR = screen->endExt = screen->rawPos;
if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
screen->eventMode = LEFTEXTENSION;
screen->startExt = *cell;
} else {
screen->eventMode = RIGHTEXTENSION;
screen->endExt = *cell;
}
ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
}
static void
EndExtend(XtermWidget xw,
XEvent * event,
String * params,
Cardinal num_params,
Bool use_cursor_loc)
{
CELL cell;
unsigned count;
TScreen *screen = TScreenOf(xw);
Char line[9];
if (use_cursor_loc) {
cell = screen->cursorp;
} else {
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
}
ExtendExtend(xw, &cell);
screen->lastButtonUpTime = event->xbutton.time;
screen->lastButton = event->xbutton.button;
if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
if (screen->replyToEmacs) {
count = 0;
if (screen->control_eight_bits) {
line[count++] = ANSI_CSI;
} else {
line[count++] = ANSI_ESC;
line[count++] = '[';
}
if (isSameCELL(&(screen->rawPos), &(screen->startSel))
&& isSameCELL(&cell, &(screen->endSel))) {
line[count++] = 't';
line[count++] = CharOf(' ' + screen->endSel.col + 1);
line[count++] = CharOf(' ' + screen->endSel.row + 1);
} else {
line[count++] = 'T';
line[count++] = CharOf(' ' + screen->startSel.col + 1);
line[count++] = CharOf(' ' + screen->startSel.row + 1);
line[count++] = CharOf(' ' + screen->endSel.col + 1);
line[count++] = CharOf(' ' + screen->endSel.row + 1);
line[count++] = CharOf(' ' + cell.col + 1);
line[count++] = CharOf(' ' + cell.row + 1);
}
v_write(screen->respond, line, count);
TrackText(xw, &zeroCELL, &zeroCELL);
}
}
SelectSet(xw, event, params, num_params);
screen->eventMode = NORMAL;
}
void
HandleSelectSet(Widget w,
XEvent * event,
String * params,
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
SelectSet(xw, event, params, *num_params);
}
}
static void
SelectSet(XtermWidget xw,
XEvent * event GCC_UNUSED,
String * params,
Cardinal num_params)
{
TScreen *screen = TScreenOf(xw);
TRACE(("SelectSet\n"));
if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
SaltTextAway(xw, &(screen->startSel), &(screen->endSel), params, num_params);
} else {
DisownSelection(xw);
}
}
#define Abs(x) ((x) < 0 ? -(x) : (x))
static void
do_start_extend(XtermWidget xw,
XEvent * event,
String * params GCC_UNUSED,
Cardinal *num_params GCC_UNUSED,
Bool use_cursor_loc)
{
TScreen *screen = TScreenOf(xw);
int coord;
CELL cell;
if (SendMousePosition(xw, event))
return;
screen->firstValidRow = 0;
screen->lastValidRow = screen->max_row;
#if OPT_READLINE
if ((KeyModifiers & ShiftMask)
|| event->xbutton.button != Button3
|| !(SCREEN_FLAG(screen, dclick3_deletes)))
#endif
screen->selectUnit = EvalSelectUnit(xw,
event->xbutton.time,
screen->selectUnit,
event->xbutton.button);
screen->replyToEmacs = False;
#if OPT_READLINE
CheckSecondPress3(screen, event);
#endif
if (screen->numberOfClicks == 1
|| (SCREEN_FLAG(screen, dclick3_deletes)
&&!(KeyModifiers & ShiftMask))) {
screen->saveStartR = screen->startExt = screen->startRaw;
screen->saveEndR = screen->endExt = screen->endRaw;
} else {
screen->startExt = screen->startRaw = screen->saveStartR;
screen->endExt = screen->endRaw = screen->saveEndR;
}
if (use_cursor_loc) {
cell = screen->cursorp;
} else {
PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
}
coord = Coordinate(screen, &cell);
if (Abs(coord - Coordinate(screen, &(screen->startSel)))
< Abs(coord - Coordinate(screen, &(screen->endSel)))
|| coord < Coordinate(screen, &(screen->startSel))) {
screen->eventMode = LEFTEXTENSION;
screen->startExt = cell;
} else {
screen->eventMode = RIGHTEXTENSION;
screen->endExt = cell;
}
ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
#if OPT_READLINE
if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
ExtendingSelection = 1;
#endif
}
static void
ExtendExtend(XtermWidget xw, const CELL * cell)
{
TScreen *screen = TScreenOf(xw);
int coord = Coordinate(screen, cell);
TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
if (screen->eventMode == LEFTEXTENSION
&& ((coord + (screen->selectUnit != Select_CHAR))
> Coordinate(screen, &(screen->endSel)))) {
screen->eventMode = RIGHTEXTENSION;
screen->startExt = screen->saveStartR;
} else if (screen->eventMode == RIGHTEXTENSION
&& coord < Coordinate(screen, &(screen->startSel))) {
screen->eventMode = LEFTEXTENSION;
screen->endExt = screen->saveEndR;
}
if (screen->eventMode == LEFTEXTENSION) {
screen->startExt = *cell;
} else {
screen->endExt = *cell;
}
ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
#if OPT_READLINE
if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
ExtendingSelection = 1;
#endif
}
void
HandleStartExtend(Widget w,
XEvent * event,
String * params,
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
do_start_extend(xw, event, params, num_params, False);
}
}
void
HandleKeyboardStartExtend(Widget w,
XEvent * event,
String * params,
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
do_start_extend(xw, event, params, num_params, True);
}
}
void
ScrollSelection(TScreen * screen, int amount, Bool always)
{
int minrow = INX2ROW(screen, -screen->savedlines);
int maxrow = INX2ROW(screen, screen->max_row);
int maxcol = screen->max_col;
#define scroll_update_one(cell) \
(cell)->row += amount; \
if ((cell)->row < minrow) { \
(cell)->row = minrow; \
(cell)->col = 0; \
} \
if ((cell)->row > maxrow) { \
(cell)->row = maxrow; \
(cell)->col = maxcol; \
}
scroll_update_one(&(screen->startRaw));
scroll_update_one(&(screen->endRaw));
scroll_update_one(&(screen->startSel));
scroll_update_one(&(screen->endSel));
scroll_update_one(&(screen->rawPos));
if (ScrnHaveSelection(screen)) {
int adjust;
adjust = ROW2INX(screen, screen->startH.row);
if (always
|| !ScrnHaveLineMargins(screen)
|| ScrnIsLineInMargins(screen, adjust)) {
scroll_update_one(&screen->startH);
}
adjust = ROW2INX(screen, screen->endH.row);
if (always
|| !ScrnHaveLineMargins(screen)
|| ScrnIsLineInMargins(screen, adjust)) {
scroll_update_one(&screen->endH);
}
}
screen->startHCoord = Coordinate(screen, &screen->startH);
screen->endHCoord = Coordinate(screen, &screen->endH);
}
void
ResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
{
rows--;
cols--;
if (screen->startRaw.row > rows)
screen->startRaw.row = rows;
if (screen->startSel.row > rows)
screen->startSel.row = rows;
if (screen->endRaw.row > rows)
screen->endRaw.row = rows;
if (screen->endSel.row > rows)
screen->endSel.row = rows;
if (screen->rawPos.row > rows)
screen->rawPos.row = rows;
if (screen->startRaw.col > cols)
screen->startRaw.col = cols;
if (screen->startSel.col > cols)
screen->startSel.col = cols;
if (screen->endRaw.col > cols)
screen->endRaw.col = cols;
if (screen->endSel.col > cols)
screen->endSel.col = cols;
if (screen->rawPos.col > cols)
screen->rawPos.col = cols;
}
#if OPT_WIDE_CHARS
Bool
iswide(int i)
{
return (i == HIDDEN_CHAR) || (my_wcwidth(i) == 2);
}
#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
#endif
static void
PointToCELL(TScreen * screen,
int y,
int x,
CELL * cell)
{
cell->row = (y - screen->border) / FontHeight(screen);
if (cell->row < screen->firstValidRow)
cell->row = screen->firstValidRow;
else if (cell->row > screen->lastValidRow)
cell->row = screen->lastValidRow;
cell->col = (x - OriginX(screen)) / FontWidth(screen);
if (cell->col < 0)
cell->col = 0;
else if (cell->col > MaxCols(screen)) {
cell->col = MaxCols(screen);
}
#if OPT_WIDE_CHARS
if (cell->col > 0
&& isWideCell(cell->row, cell->col - 1)
&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
cell->col -= 1;
}
#endif
}
static int
LastTextCol(TScreen * screen, int row)
{
int inx = ROW2INX(screen, row);
int i;
Char *ch;
if (inx + screen->savedlines >= 0) {
for (i = screen->max_col,
ch = SCRN_BUF_ATTRS(screen, inx) + i;
i >= 0 && !(*ch & CHARDRAWN);
ch--, i--) ;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, inx)[0])) {
i *= 2;
}
#endif
} else {
i = -1;
}
return (i);
}
#if !OPT_WIDE_CHARS
static int charClass[256] =
{
32, 1, 1, 1, 1, 1, 1, 1,
1, 32, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 58, 59, 60, 61, 62, 63,
64, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 91, 92, 93, 94, 48,
96, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 123, 124, 125, 126, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183,
184, 185, 186, 187, 188, 189, 190, 191,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 215,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 247,
48, 48, 48, 48, 48, 48, 48, 48};
int
SetCharacterClassRange(int low,
int high,
int value)
{
if (low < 0 || high > 255 || high < low)
return (-1);
for (; low <= high; low++)
charClass[low] = value;
return (0);
}
#endif
#if OPT_WIDE_CHARS
static int
class_of(TScreen * screen, CELL * cell)
{
CELL temp = *cell;
int value;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, ROW2INX(screen, temp.row))[0])) {
temp.col /= 2;
}
#endif
value = (int) XTERM_CELL(temp.row, temp.col);
if_OPT_WIDE_CHARS(screen, {
return CharacterClass(value);
});
return CharacterClass(value);
}
#define ClassSelects(screen, cell, cclass) \
(class_of(screen, cell) == cclass \
|| XTERM_CELL((cell)->row, (cell)->col) == HIDDEN_CHAR)
#else
#define class_of(screen, cell) charClass[XTERM_CELL((cell)->row, (cell)->col)]
#define ClassSelects(screen, cell, cclass) \
(class_of(screen, (cell)) == cclass)
#endif
static Boolean
okPosition(TScreen * screen,
CELL * cell)
{
if (cell->col > (LastTextCol(screen, cell->row) + 1)) {
cell->col = 0;
cell->row += 1;
return False;
}
return True;
}
static void
trimLastLine(TScreen * screen, CELL * last)
{
if (screen->cutNewline) {
last->col = 0;
++last->row;
} else {
last->col = LastTextCol(screen, last->row) + 1;
}
}
#if OPT_SELECT_REGEX
static int
firstRowOfLine(TScreen * screen, int row, Bool visible)
{
int limit = visible ? 0 : -screen->savedlines;
while (row > limit &&
ScrnTstWrapped(screen, row - 1))
--row;
return row;
}
static int
lastRowOfLine(TScreen * screen, int row)
{
while (row < screen->max_row &&
ScrnTstWrapped(screen, row))
++row;
return row;
}
static unsigned
lengthOfLines(TScreen * screen, int firstRow, int lastRow)
{
unsigned length = 0;
int n;
for (n = firstRow; n <= lastRow; ++n) {
int value = LastTextCol(screen, n);
if (value >= 0)
length += (unsigned) (value + 1);
}
return length;
}
static char *
make_indexed_text(TScreen * screen, int row, unsigned length, int *indexed)
{
Char *result = 0;
unsigned need = (length + 1);
if_OPT_WIDE_CHARS(screen, {
need *= (unsigned) (MAX_PTRS * 6);
});
if ((result = TypeCallocN(Char, need + 1)) != 0) {
unsigned used = 0;
Char *last = result;
do {
int col = 0;
int limit = LastTextCol(screen, row);
while (col <= limit) {
Char *next = last;
unsigned data = XTERM_CELL(row, col);
if (data == 0)
data = ' ';
if_WIDE_OR_NARROW(screen, {
next = convertToUTF8(last, data);
}
, {
*next++ = CharOf(data);
});
if_OPT_WIDE_CHARS(screen, {
int off;
for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
if ((data = XTERM_CELLC(row, col, off)) == 0)
break;
next = convertToUTF8(next, data);
}
});
indexed[used] = last - result;
*next = 0;
last = next;
++used;
++col;
indexed[used] = next - result;
}
} while (used < length &&
ScrnTstWrapped(screen, row) &&
++row < screen->max_row);
}
return (char *) result;
}
static int
indexToCol(int *indexed, int len, int off)
{
int col = 0;
while (indexed[col] < len) {
if (indexed[col] >= off)
break;
++col;
}
return col;
}
static void
columnToCell(TScreen * screen, int row, int col, CELL * cell)
{
while (row < screen->max_row) {
int last = LastTextCol(screen, row);
if (col <= last) {
break;
}
if (!ScrnTstWrapped(screen, row)) {
col = last + 1;
break;
}
col -= (last + 1);
++row;
}
if (col < 0)
col = 0;
cell->row = row;
cell->col = col;
}
static int
cellToColumn(TScreen * screen, CELL * cell)
{
int col = cell->col;
int row = firstRowOfLine(screen, cell->row, False);
while (row < cell->row) {
col += LastTextCol(screen, row++);
}
return col;
}
static void
do_select_regex(TScreen * screen, CELL * startc, CELL * endc)
{
int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
char *expr = screen->selectExpr[inx];
regex_t preg;
regmatch_t match;
char *search;
int *indexed;
TRACE(("Select_REGEX:%s\n", NonNull(expr)));
if (okPosition(screen, startc) && expr != 0) {
if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
int firstRow = firstRowOfLine(screen, startc->row, True);
int lastRow = lastRowOfLine(screen, firstRow);
unsigned size = lengthOfLines(screen, firstRow, lastRow);
int actual = cellToColumn(screen, startc);
TRACE(("regcomp ok rows %d..%d bytes %d\n",
firstRow, lastRow, size));
if ((indexed = TypeCallocN(int, size + 1)) != 0) {
if ((search = make_indexed_text(screen,
firstRow,
size,
indexed)) != 0) {
int len = (int) strlen(search);
int col;
int best_col = -1;
int best_len = -1;
for (col = 0; indexed[col] < len; ++col) {
if (regexec(&preg,
search + indexed[col],
1, &match, 0) == 0) {
int start_inx = match.rm_so + indexed[col];
int finis_inx = match.rm_eo + indexed[col];
int start_col = indexToCol(indexed, len, start_inx);
int finis_col = indexToCol(indexed, len, finis_inx);
if (start_col <= actual &&
actual < finis_col) {
int test = finis_col - start_col;
if (best_len < test) {
best_len = test;
best_col = start_col;
TRACE(("match column %d len %d\n",
best_col,
best_len));
}
}
}
}
if (best_col >= 0) {
int best_nxt = best_col + best_len;
columnToCell(screen, firstRow, best_col, startc);
columnToCell(screen, firstRow, best_nxt, endc);
TRACE(("search::%s\n", search));
TRACE(("indexed:%d..%d -> %d..%d\n",
best_col, best_nxt,
indexed[best_col],
indexed[best_nxt]));
TRACE(("matched:%d:%s\n",
indexed[best_nxt] + 1 -
indexed[best_col],
visibleChars(PAIRED_CHARS((Char *) (search +
indexed[best_col]),
0),
(unsigned) (indexed[best_nxt] +
1 -
indexed[best_col]))));
}
free(search);
}
free(indexed);
}
regfree(&preg);
}
}
}
#endif
static void
ComputeSelect(XtermWidget xw,
CELL * startc,
CELL * endc,
Bool extend)
{
TScreen *screen = TScreenOf(xw);
int length;
int cclass;
CELL first = *startc;
CELL last = *endc;
TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
first.row, first.col,
last.row, last.col,
extend ? "" : "no"));
#if OPT_WIDE_CHARS
if (first.col > 1
&& isWideCell(first.row, first.col - 1)
&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
fprintf(stderr, "Adjusting start. Changing downwards from %i.\n", first.col);
first.col -= 1;
if (last.col == (first.col + 1))
last.col--;
}
if (last.col > 1
&& isWideCell(last.row, last.col - 1)
&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
last.col += 1;
}
#endif
if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
screen->startSel = screen->startRaw = first;
screen->endSel = screen->endRaw = last;
} else {
screen->startSel = screen->startRaw = last;
screen->endSel = screen->endRaw = first;
}
switch (screen->selectUnit) {
case Select_CHAR:
(void) okPosition(screen, &(screen->startSel));
(void) okPosition(screen, &(screen->endSel));
break;
case Select_WORD:
TRACE(("Select_WORD\n"));
if (okPosition(screen, &(screen->startSel))) {
cclass = class_of(screen, &(screen->startSel));
do {
--screen->startSel.col;
if (screen->startSel.row > 0
&& screen->startSel.col < 0
&& ScrnTstWrapped(screen, screen->startSel.row - 1)) {
--screen->startSel.row;
screen->startSel.col = LastTextCol(screen, screen->startSel.row);
}
} while (screen->startSel.col >= 0
&& ClassSelects(screen, &(screen->startSel), cclass));
++screen->startSel.col;
}
#if OPT_WIDE_CHARS
if (screen->startSel.col
&& XTERM_CELL(screen->startSel.row,
screen->startSel.col) == HIDDEN_CHAR)
screen->startSel.col++;
#endif
if (okPosition(screen, &(screen->endSel))) {
length = LastTextCol(screen, screen->endSel.row);
cclass = class_of(screen, &(screen->endSel));
do {
++screen->endSel.col;
if (screen->endSel.col > length
&& ScrnTstWrapped(screen, screen->endSel.row)) {
screen->endSel.col = 0;
++screen->endSel.row;
length = LastTextCol(screen, screen->endSel.row);
}
} while (screen->endSel.col <= length
&& ClassSelects(screen, &(screen->endSel), cclass));
if (screen->endSel.col > length + 1) {
screen->endSel.col = 0;
++screen->endSel.row;
}
}
#if OPT_WIDE_CHARS
if (screen->endSel.col
&& XTERM_CELL(screen->endSel.row,
screen->endSel.col) == HIDDEN_CHAR)
screen->endSel.col++;
#endif
screen->saveStartW = screen->startSel;
break;
case Select_LINE:
TRACE(("Select_LINE\n"));
while (ScrnTstWrapped(screen, screen->endSel.row)) {
++screen->endSel.row;
}
if (screen->cutToBeginningOfLine
|| screen->startSel.row < screen->saveStartW.row) {
screen->startSel.col = 0;
while (screen->startSel.row > 0
&& ScrnTstWrapped(screen, screen->startSel.row - 1)) {
--screen->startSel.row;
}
} else if (!extend) {
if ((first.row < screen->saveStartW.row)
|| (isSameRow(&first, &(screen->saveStartW))
&& first.col < screen->saveStartW.col)) {
screen->startSel.col = 0;
while (screen->startSel.row > 0
&& ScrnTstWrapped(screen, screen->startSel.row - 1)) {
--screen->startSel.row;
}
} else {
screen->startSel = screen->saveStartW;
}
}
trimLastLine(screen, &(screen->endSel));
break;
case Select_GROUP:
TRACE(("Select_GROUP\n"));
if (okPosition(screen, &(screen->startSel))) {
while (screen->startSel.row > 0 &&
(LastTextCol(screen, screen->startSel.row - 1) > 0 ||
ScrnTstWrapped(screen, screen->startSel.row - 1))) {
--screen->startSel.row;
}
screen->startSel.col = 0;
while (screen->endSel.row < screen->max_row &&
(LastTextCol(screen, screen->endSel.row + 1) > 0 ||
ScrnTstWrapped(screen, screen->endSel.row))) {
++screen->endSel.row;
}
trimLastLine(screen, &(screen->endSel));
}
break;
case Select_PAGE:
TRACE(("Select_PAGE\n"));
screen->startSel.row = 0;
screen->startSel.col = 0;
screen->endSel.row = screen->max_row + 1;
screen->endSel.col = 0;
break;
case Select_ALL:
TRACE(("Select_ALL\n"));
screen->startSel.row = -screen->savedlines;
screen->startSel.col = 0;
screen->endSel.row = screen->max_row + 1;
screen->endSel.col = 0;
break;
#if OPT_SELECT_REGEX
case Select_REGEX:
do_select_regex(screen, &(screen->startSel), &(screen->endSel));
break;
#endif
case NSELECTUNITS:
return;
}
ScrollSelection(screen, 0, False);
TrackText(xw, &(screen->startSel), &(screen->endSel));
return;
}
static void
TrackText(XtermWidget xw,
const CELL * firstp,
const CELL * lastp)
{
TScreen *screen = TScreenOf(xw);
int from, to;
CELL old_start, old_end;
CELL first = *firstp;
CELL last = *lastp;
TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
first.row, first.col, last.row, last.col));
old_start = screen->startH;
old_end = screen->endH;
if (isSameCELL(&first, &old_start) &&
isSameCELL(&last, &old_end))
return;
screen->startH = first;
screen->endH = last;
from = Coordinate(screen, &screen->startH);
to = Coordinate(screen, &screen->endH);
if (to <= screen->startHCoord || from > screen->endHCoord) {
ReHiliteText(xw, &old_start, &old_end);
ReHiliteText(xw, &first, &last);
} else {
if (from < screen->startHCoord) {
ReHiliteText(xw, &first, &old_start);
} else if (from > screen->startHCoord) {
ReHiliteText(xw, &old_start, &first);
}
if (to > screen->endHCoord) {
ReHiliteText(xw, &old_end, &last);
} else if (to < screen->endHCoord) {
ReHiliteText(xw, &last, &old_end);
}
}
screen->startHCoord = from;
screen->endHCoord = to;
}
static void
ReHiliteText(XtermWidget xw,
CELL * firstp,
CELL * lastp)
{
TScreen *screen = TScreenOf(xw);
int i;
CELL first = *firstp;
CELL last = *lastp;
TRACE(("ReHiliteText from %d.%d to %d.%d\n",
first.row, first.col, last.row, last.col));
if (first.row < 0)
first.row = first.col = 0;
else if (first.row > screen->max_row)
return;
if (last.row < 0)
return;
else if (last.row > screen->max_row) {
last.row = screen->max_row;
last.col = MaxCols(screen);
}
if (isSameCELL(&first, &last))
return;
if (!isSameRow(&first, &last)) {
if ((i = screen->max_col - first.col + 1) > 0) {
ScrnRefresh(xw, first.row, first.col, 1, i, True);
}
if ((i = last.row - first.row - 1) > 0) {
ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
}
if (last.col > 0 && last.row <= screen->max_row) {
ScrnRefresh(xw, last.row, 0, 1, last.col, True);
}
} else {
ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
}
}
static void
SaltTextAway(XtermWidget xw,
CELL * cellc,
CELL * cell,
String * params,
Cardinal num_params)
{
TScreen *screen = TScreenOf(xw);
int i, j = 0;
int eol;
Char *line;
Char *lp;
CELL first = *cellc;
CELL last = *cell;
if (isSameRow(&first, &last) && first.col > last.col) {
int tmp = first.col;
first.col = last.col;
last.col = tmp;
}
--last.col;
if (isSameRow(&last, &first)) {
j = Length(screen, first.row, first.col, last.col);
} else {
j += Length(screen, first.row, first.col, screen->max_col) + 1;
for (i = first.row + 1; i < last.row; i++)
j += Length(screen, i, 0, screen->max_col) + 1;
if (last.col >= 0)
j += Length(screen, last.row, 0, last.col);
}
if_OPT_WIDE_CHARS(screen, {
j *= 4;
});
if (screen->selection_size <= j) {
if ((line = (Char *) malloc((unsigned) j + 1)) == 0)
SysError(ERROR_BMALLOC2);
XtFree((char *) screen->selection_data);
screen->selection_data = line;
screen->selection_size = j + 1;
} else {
line = screen->selection_data;
}
if ((line == 0)
|| (j < 0))
return;
line[j] = '\0';
lp = line;
if (isSameRow(&last, &first)) {
lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
} else {
lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
if (eol)
*lp++ = '\n';
for (i = first.row + 1; i < last.row; i++) {
lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
if (eol)
*lp++ = '\n';
}
if (last.col >= 0)
lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
}
*lp = '\0';
TRACE(("Salted TEXT:%d:%s\n", lp - line,
visibleChars(PAIRED_CHARS(line, 0), (unsigned) (lp - line))));
screen->selection_length = (unsigned long) (lp - line);
_OwnSelection(xw, params, num_params);
}
#if OPT_PASTE64
void
ClearSelectionBuffer(TScreen * screen)
{
screen->selection_length = 0;
screen->base64_count = 0;
}
static void
AppendStrToSelectionBuffer(TScreen * screen, Char * text, unsigned len)
{
if (len != 0) {
int j = (int) (screen->selection_length + len);
int k = j + (j >> 2) + 80;
if (j + 1 >= screen->selection_size) {
if (!screen->selection_length) {
Char *line;
if ((line = (Char *) malloc((unsigned) k)) == 0)
SysError(ERROR_BMALLOC2);
XtFree((char *) screen->selection_data);
screen->selection_data = line;
} else {
screen->selection_data = (Char *)
realloc(screen->selection_data,
(unsigned) k);
if (screen->selection_data == 0)
SysError(ERROR_BMALLOC2);
}
screen->selection_size = k;
}
memcpy(screen->selection_data + screen->selection_length, text, len);
screen->selection_length += len;
screen->selection_data[screen->selection_length] = 0;
}
}
void
AppendToSelectionBuffer(TScreen * screen, unsigned c)
{
unsigned six;
Char ch;
if (c >= 'A' && c <= 'Z')
six = c - 'A';
else if (c >= 'a' && c <= 'z')
six = c - 'a' + 26;
else if (c >= '0' && c <= '9')
six = c - '0' + 52;
else if (c == '+')
six = 62;
else if (c == '/')
six = 63;
else
return;
switch (screen->base64_count) {
case 0:
screen->base64_accu = six;
screen->base64_count = 6;
break;
case 2:
ch = CharOf((screen->base64_accu << 6) + six);
screen->base64_count = 0;
AppendStrToSelectionBuffer(screen, &ch, 1);
break;
case 4:
ch = CharOf((screen->base64_accu << 4) + (six >> 2));
screen->base64_accu = (six & 0x3);
screen->base64_count = 2;
AppendStrToSelectionBuffer(screen, &ch, 1);
break;
case 6:
ch = CharOf((screen->base64_accu << 2) + (six >> 4));
screen->base64_accu = (six & 0xF);
screen->base64_count = 4;
AppendStrToSelectionBuffer(screen, &ch, 1);
break;
}
}
void
CompleteSelection(XtermWidget xw, char **args, Cardinal len)
{
TScreen *screen = TScreenOf(xw);
screen->base64_count = 0;
screen->base64_accu = 0;
_OwnSelection(xw, args, len);
}
#endif
static Bool
_ConvertSelectionHelper(Widget w,
Atom * type,
XtPointer *value,
unsigned long *length,
int *format,
int (*conversion_function) (Display *,
char **, int,
XICCEncodingStyle,
XTextProperty *),
XICCEncodingStyle conversion_style)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
Display *dpy = XtDisplay(w);
XTextProperty textprop;
char *the_data = (char *) screen->selection_data;
if (conversion_function(dpy, &the_data, 1,
conversion_style,
&textprop) >= Success) {
*value = (XtPointer) textprop.value;
*length = textprop.nitems;
*type = textprop.encoding;
*format = textprop.format;
return True;
}
}
return False;
}
static Boolean
SaveConvertedLength(XtPointer *target, unsigned long source)
{
Boolean result = False;
*target = XtMalloc(4);
if (*target != 0) {
result = True;
if (sizeof(unsigned long) == 4) {
*(unsigned long *) *target = source;
} else if (sizeof(unsigned) == 4) {
*(unsigned *) *target = source;
} else if (sizeof(unsigned short) == 4) {
*(unsigned short *) *target = (unsigned short) source;
} else {
unsigned long temp = source;
memcpy((char *) *target, ((char *) &temp) + sizeof(temp) - 4, 4);
}
}
return result;
}
static Boolean
ConvertSelection(Widget w,
Atom * selection,
Atom * target,
Atom * type,
XtPointer *value,
unsigned long *length,
int *format)
{
Display *dpy = XtDisplay(w);
TScreen *screen;
Bool result = False;
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0)
return False;
screen = TScreenOf(xw);
if (screen->selection_data == NULL)
return False;
if (*target == XA_TARGETS(dpy)) {
Atom *allocP;
Atom *targetP;
Atom *std_targets;
XPointer std_return = 0;
unsigned long std_length;
TRACE(("ConvertSelection XA_TARGETS(dpy)\n"));
if (XmuConvertStandardSelection(w, screen->selection_time, selection,
target, type, &std_return,
&std_length, format)) {
std_targets = (Atom *) (std_return);
*length = std_length + 6;
targetP = (Atom *) XtMalloc(sizeof(Atom) * (*length));
allocP = targetP;
*value = (XtPointer) targetP;
*targetP++ = XA_STRING;
*targetP++ = XA_TEXT(dpy);
#ifdef X_HAVE_UTF8_STRING
*targetP++ = XA_COMPOUND_TEXT(dpy);
*targetP++ = XA_UTF8_STRING(dpy);
#else
*targetP = XA_COMPOUND_TEXT(dpy);
if_OPT_WIDE_CHARS(screen, {
*targetP = XA_UTF8_STRING(dpy);
});
targetP++;
#endif
*targetP++ = XA_LENGTH(dpy);
*targetP++ = XA_LIST_LENGTH(dpy);
*length = std_length + (unsigned long) (targetP - allocP);
memcpy(targetP, std_targets, sizeof(Atom) * std_length);
XtFree((char *) std_targets);
*type = XA_ATOM;
*format = 32;
result = True;
}
}
#if OPT_WIDE_CHARS
else if (screen->wide_chars && *target == XA_STRING) {
TRACE(("ConvertSelection XA_STRING - wide\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XStringStyle);
} else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
TRACE(("ConvertSelection XA_UTF8_STRING(dpy) - wide\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XUTF8StringStyle);
} else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
TRACE(("ConvertSelection XA_TEXT(dpy) - wide\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XStdICCTextStyle);
} else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
TRACE(("ConvertSelection XA_COMPOUND_TEXT(dpy) - wide\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XCompoundTextStyle);
}
#endif
else if (*target == XA_STRING) {
TRACE(("ConvertSelection XA_STRING\n"));
*type = XA_STRING;
*value = (XtPointer) screen->selection_data;
*length = screen->selection_length;
*format = 8;
result = True;
} else if (*target == XA_TEXT(dpy)) {
TRACE(("ConvertSelection XA_TEXT(dpy)\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
XmbTextListToTextProperty,
XStdICCTextStyle);
} else if (*target == XA_COMPOUND_TEXT(dpy)) {
TRACE(("ConvertSelection XA_COMPOUND_TEXT(dpy)\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
XmbTextListToTextProperty,
XCompoundTextStyle);
}
#ifdef X_HAVE_UTF8_STRING
else if (*target == XA_UTF8_STRING(dpy)) {
TRACE(("ConvertSelection XA_UTF8_STRING(dpy)\n"));
result =
_ConvertSelectionHelper(w,
type, value, length, format,
XmbTextListToTextProperty,
XUTF8StringStyle);
}
#endif
else if (*target == XA_LIST_LENGTH(dpy)) {
TRACE(("ConvertSelection XA_LIST_LENGTH(dpy)\n"));
result = SaveConvertedLength(value, 1);
*type = XA_INTEGER;
*length = 1;
*format = 32;
} else if (*target == XA_LENGTH(dpy)) {
TRACE(("ConvertSelection XA_LENGTH(dpy)\n"));
result = SaveConvertedLength(value, screen->selection_length);
*type = XA_INTEGER;
*length = 1;
*format = 32;
} else if (XmuConvertStandardSelection(w,
screen->selection_time, selection,
target, type, (XPointer *) value,
length, format)) {
TRACE(("ConvertSelection XmuConvertStandardSelection\n"));
result = True;
}
return (Boolean) result;
}
static void
LoseSelection(Widget w, Atom * selection)
{
TScreen *screen;
Atom *atomP;
Cardinal i;
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0)
return;
screen = TScreenOf(xw);
for (i = 0, atomP = screen->selection_atoms;
i < screen->selection_count; i++, atomP++) {
if (*selection == *atomP)
*atomP = (Atom) 0;
if (CutBuffer(*atomP) >= 0) {
*atomP = (Atom) 0;
}
}
for (i = screen->selection_count; i; i--) {
if (screen->selection_atoms[i - 1] != 0)
break;
}
screen->selection_count = i;
for (i = 0, atomP = screen->selection_atoms;
i < screen->selection_count; i++, atomP++) {
if (*atomP == (Atom) 0) {
*atomP = screen->selection_atoms[--screen->selection_count];
}
}
if (screen->selection_count == 0)
TrackText(xw, &zeroCELL, &zeroCELL);
}
static void
SelectionDone(Widget w GCC_UNUSED,
Atom * selection GCC_UNUSED,
Atom * target GCC_UNUSED)
{
}
static void
_OwnSelection(XtermWidget xw,
String * selections,
Cardinal count)
{
TScreen *screen = TScreenOf(xw);
Atom *atoms = screen->selection_atoms;
Cardinal i;
Bool have_selection = False;
if (screen->selection_length == 0)
return;
TRACE(("_OwnSelection\n"));
selections = MapSelections(xw, selections, count);
if (count > screen->sel_atoms_size) {
XtFree((char *) atoms);
atoms = (Atom *) XtMalloc(count * sizeof(Atom));
screen->selection_atoms = atoms;
screen->sel_atoms_size = count;
}
XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
for (i = 0; i < count; i++) {
int cutbuffer = CutBuffer(atoms[i]);
if (cutbuffer >= 0) {
unsigned long limit =
(unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
if (screen->selection_length > limit) {
fprintf(stderr,
"%s: selection too big (%ld bytes), not storing in CUT_BUFFER%d\n",
xterm_name, screen->selection_length, cutbuffer);
} else {
unsigned long length = screen->selection_length;
Char *data = screen->selection_data;
if_OPT_WIDE_CHARS((screen), {
data = UTF8toLatin1(data, length, &length);
});
TRACE(("XStoreBuffer(%d)\n", cutbuffer));
XStoreBuffer(XtDisplay((Widget) xw),
(char *) data,
(int) length,
cutbuffer);
}
} else if (!screen->replyToEmacs) {
have_selection |=
XtOwnSelection((Widget) xw, atoms[i],
screen->selection_time,
ConvertSelection, LoseSelection, SelectionDone);
}
}
if (!screen->replyToEmacs)
screen->selection_count = count;
if (!have_selection)
TrackText(xw, &zeroCELL, &zeroCELL);
}
static void
ResetSelectionState(TScreen * screen)
{
screen->selection_count = 0;
screen->startH = zeroCELL;
screen->endH = zeroCELL;
}
void
DisownSelection(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
Atom *atoms = screen->selection_atoms;
Cardinal count = screen->selection_count;
Cardinal i;
TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
count,
screen->startH.row,
screen->startH.col,
screen->endH.row,
screen->endH.col));
for (i = 0; i < count; i++) {
int cutbuffer = CutBuffer(atoms[i]);
if (cutbuffer < 0) {
XtDisownSelection((Widget) xw, atoms[i],
screen->selection_time);
}
}
if (ScrnHaveSelection(screen)) {
CELL first = screen->startH;
CELL last = screen->endH;
ResetSelectionState(screen);
ReHiliteText(xw, &first, &last);
} else {
ResetSelectionState(screen);
}
}
void
UnhiliteSelection(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
if (ScrnHaveSelection(screen)) {
CELL first = screen->startH;
CELL last = screen->endH;
screen->startH = zeroCELL;
screen->endH = zeroCELL;
ReHiliteText(xw, &first, &last);
}
}
static int
Length(TScreen * screen GCC_UNUSED,
int row,
int scol,
int ecol)
{
int lastcol = LastTextCol(screen, row);
if (ecol > lastcol)
ecol = lastcol;
return (ecol - scol + 1);
}
static Char *
SaveText(TScreen * screen,
int row,
int scol,
int ecol,
Char * lp,
int *eol)
{
int i = 0;
unsigned c;
Char *result = lp;
#if OPT_WIDE_CHARS
unsigned previous = 0;
#endif
i = Length(screen, row, scol, ecol);
ecol = scol + i;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, ROW2INX(screen, row))[0])) {
scol = (scol + 0) / 2;
ecol = (ecol + 1) / 2;
}
#endif
*eol = !ScrnTstWrapped(screen, row);
for (i = scol; i < ecol; i++) {
c = E2A(XTERM_CELL(row, i));
#if OPT_WIDE_CHARS
if (c == HIDDEN_CHAR && iswide((int) previous)) {
previous = c;
if_OPT_WIDE_CHARS(screen, {
if (screen->utf8_mode != uFalse) {
unsigned ch;
int off;
for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
if ((ch = XTERM_CELLC(row, i, off)) == 0)
break;
lp = convertToUTF8(lp, ch);
}
}
});
continue;
}
previous = c;
if (screen->utf8_mode != uFalse) {
lp = convertToUTF8(lp, (c != 0) ? c : ' ');
if_OPT_WIDE_CHARS(screen, {
unsigned ch;
int off;
for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
if ((ch = XTERM_CELLC(row, i, off)) == 0)
break;
lp = convertToUTF8(lp, ch);
}
});
} else
#endif
{
if (c == 0) {
c = E2A(' ');
} else if (c < E2A(' ')) {
c = DECtoASCII(c);
} else if (c == 0x7f) {
c = 0x5f;
}
*lp++ = CharOf(A2E(c));
}
if (c != E2A(' '))
result = lp;
}
if (!*eol || !screen->trim_selection)
result = lp;
return (result);
}
static Char
BtnCode(XButtonEvent * event, int button)
{
int result = (int) (32 + (KeyState(event->state) << 2));
if (button < 0 || button > 5) {
result += 3;
} else {
if (button > 3)
result += (64 - 4);
if (event->type == MotionNotify)
result += 32;
result += button;
}
return CharOf(result);
}
#define MOUSE_LIMIT (255 - 32)
static void
EditorButton(XtermWidget xw, XButtonEvent * event)
{
TScreen *screen = TScreenOf(xw);
int pty = screen->respond;
Char line[6];
int row, col;
int button;
unsigned count = 0;
Boolean changed = True;
button = (int) (event->button - 1);
if (button >= 3)
button++;
row = (event->y - screen->border) / FontHeight(screen);
col = (event->x - OriginX(screen)) / FontWidth(screen);
if (row < 0)
row = 0;
else if (row > screen->max_row)
row = screen->max_row;
else if (row > MOUSE_LIMIT)
row = MOUSE_LIMIT;
if (col < 0)
col = 0;
else if (col > screen->max_col)
col = screen->max_col;
else if (col > MOUSE_LIMIT)
col = MOUSE_LIMIT;
if (screen->control_eight_bits) {
line[count++] = ANSI_CSI;
} else {
line[count++] = ANSI_ESC;
line[count++] = '[';
}
#if OPT_SCO_FUNC_KEYS
if (xw->keyboard.type == keyboardIsSCO) {
line[count++] = '>';
}
#endif
line[count++] = 'M';
if (screen->send_mouse_pos == X10_MOUSE) {
line[count++] = CharOf(' ' + button);
} else {
switch (event->type) {
case ButtonPress:
line[count++] = BtnCode(event, screen->mouse_button = button);
break;
case ButtonRelease:
if (button < 3)
button = -1;
line[count++] = BtnCode(event, screen->mouse_button = button);
break;
case MotionNotify:
if ((row == screen->mouse_row)
&& (col == screen->mouse_col)) {
changed = False;
} else {
line[count++] = BtnCode(event, screen->mouse_button);
}
break;
default:
changed = False;
break;
}
}
if (changed) {
screen->mouse_row = row;
screen->mouse_col = col;
line[count++] = CharOf(' ' + col + 1);
line[count++] = CharOf(' ' + row + 1);
TRACE(("mouse at %d,%d button+mask = %#x\n", row, col,
(screen->control_eight_bits) ? line[2] : line[3]));
v_write(pty, line, count);
}
return;
}
#if OPT_FOCUS_EVENT
void
SendFocusButton(XtermWidget xw, XFocusChangeEvent * event)
{
TScreen *screen = TScreenOf(xw);
if (screen->send_focus_pos) {
ANSI reply;
memset(&reply, 0, sizeof(reply));
reply.a_type = ANSI_CSI;
#if OPT_SCO_FUNC_KEYS
if (xw->keyboard.type == keyboardIsSCO) {
reply.a_pintro = '>';
}
#endif
reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
unparseseq(xw, &reply);
}
return;
}
#endif