actions.c 7.63 KB
/* SPDX-License-Identifier: LGPL-3.0-or-later */

/*
 * Copyright (C) 2008 Banco do Brasil S.A.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <internals.h>
#include <lib3270.h>
#include <lib3270/actions.h>
#include <lib3270/session.h>
#include <lib3270/selection.h>
#include <lib3270/log.h>
#include <lib3270/trace.h>
#include <lib3270/toggle.h>
#include "3270ds.h"

/*--[ Implement ]------------------------------------------------------------------------------------*/

LIB3270_EXPORT int lib3270_unselect(H3270 *hSession) {
	int a;

	CHECK_SESSION_HANDLE(hSession);

	trace("%s",__FUNCTION__);

	if(hSession->selected) {
		hSession->selected = 0;

		for(a = 0; a < ((int) (hSession->view.rows * hSession->view.cols)); a++) {
			if(hSession->text[a].attr & LIB3270_ATTR_SELECTED) {
				hSession->text[a].attr &= ~LIB3270_ATTR_SELECTED;
				if(hSession->cbk.update)
					hSession->cbk.update(hSession,a,hSession->text[a].chr,hSession->text[a].attr,a == hSession->cursor_addr);
			}
		}

		hSession->cbk.set_selection(hSession,0);
		hSession->cbk.update_selection(hSession,-1,-1);

		lib3270_action_group_notify(hSession,LIB3270_ACTION_GROUP_SELECTION);

	}

	return 0;
}

LIB3270_EXPORT void lib3270_select_to(H3270 *session, int baddr) {
	int start, end;

	CHECK_SESSION_HANDLE(session);

	if(!lib3270_is_connected(session))
		return;

	start = session->selected ? session->select.start : session->cursor_addr;

	cursor_move(session,end = baddr);

	do_select(session,start,end,lib3270_get_toggle(session,LIB3270_TOGGLE_RECTANGLE_SELECT));

}

LIB3270_EXPORT int lib3270_select_region(H3270 *h, int start, int end) {
	int maxlen;

	CHECK_SESSION_HANDLE(h);

	if(!lib3270_is_connected(h))
		return ENOTCONN;

	maxlen = (h->view.rows * h->view.cols);

	// Check bounds
	if(start < 0 || start > maxlen || end < 0 || end > maxlen)
		return EINVAL;

	do_select(h,start,end,lib3270_get_toggle(h,LIB3270_TOGGLE_RECTANGLE_SELECT));

	return 0;
}

LIB3270_EXPORT int lib3270_select_word(H3270 *session) {
	return lib3270_select_word_at(session,-1);
}

LIB3270_EXPORT int lib3270_select_word_at(H3270 *session, int baddr) {
	int start, end, rc;

	rc = lib3270_get_word_bounds(session,baddr,&start,&end);
	if(rc)
		return rc;

	trace("%s: baddr=%d start=%d end=%d",__FUNCTION__,baddr,start,end);

	rc = do_select(session,start,end,0);
	if(rc)
		return rc;

	// Notify word selected to allow testing for URL selection.
	session->cbk.word_selected(session,start,end);

	return 0;
}

LIB3270_EXPORT int lib3270_select_field_at(H3270 *session, int baddr) {
	int start, end;

	if(lib3270_get_field_bounds(session,baddr,&start,&end))
		return -1;

	do_select(session,start,end,0);

	return 0;
}

LIB3270_EXPORT int lib3270_select_field(H3270 *hSession) {
	CHECK_SESSION_HANDLE(hSession);
	lib3270_select_field_at(hSession,hSession->cursor_addr);
	return 0;
}

LIB3270_EXPORT int lib3270_select_all(H3270 * hSession) {
	FAIL_IF_NOT_ONLINE(hSession);

	do_select(hSession,0,(hSession->view.rows * hSession->view.cols)-1,0);

	return 0;
}

LIB3270_EXPORT int lib3270_reselect(H3270 *hSession) {
	FAIL_IF_NOT_ONLINE(hSession);

	if(hSession->select.start == hSession->select.end || hSession->selected)
		return 0;

	do_select(hSession, hSession->select.start,hSession->select.end,lib3270_get_toggle(hSession,LIB3270_TOGGLE_RECTANGLE_SELECT));

	return 0;
}

LIB3270_EXPORT int lib3270_get_selection_bounds(H3270 *hSession, int *start, int *end) {
	int first, last;

	CHECK_SESSION_HANDLE(hSession);

	if(!hSession->selected || hSession->select.start == hSession->select.end)
		return 0;

	if(hSession->select.end > hSession->select.start) {
		first = hSession->select.start;
		last  = hSession->select.end;
	} else {
		first = hSession->select.end;
		last  = hSession->select.start;
	}

	if(start)
		*start = first;

	if(end)
		*end = last;

	return 1;
}

LIB3270_EXPORT int lib3270_move_selected_area(H3270 *hSession, int from, int to) {
	int pos[2];
	int rows, cols, f, step;

	if(from == to)
		return from;

	if(!lib3270_get_selection_bounds(hSession,&pos[0],&pos[1]))
		return from;

	rows = (to / hSession->view.cols) - (from / hSession->view.cols);
	cols = (to % hSession->view.cols) - (from % hSession->view.cols);

	for(f=0; f<2; f++) {
		int row  = (pos[f] / hSession->view.cols) + rows;
		int col  = (pos[f] % hSession->view.cols) + cols;

		if(row < 0)
			rows = - (pos[f] / hSession->view.cols);

		if(col < 0)
			cols = - (pos[f] % hSession->view.cols);

		if(row >= ((int) hSession->view.rows))
			rows = hSession->view.rows - ((pos[f] / hSession->view.cols)+1);

		if(col >= ((int) hSession->view.cols))
			cols = hSession->view.cols - ((pos[f] % hSession->view.cols)+1);
	}

	step = (rows * hSession->view.cols) + cols;

	do_select(hSession,hSession->select.start + step,hSession->select.end + step,hSession->rectsel);
	cursor_move(hSession,hSession->select.end);

	return from+step;
}

LIB3270_EXPORT int lib3270_drag_selection(H3270 *h, unsigned char flag, int origin, int baddr) {
	int first, last, row, col;

	if(!lib3270_get_selection_bounds(h,&first,&last))
		return origin;

	/*
		trace("%s: flag=%04x %s %s %s %s",__FUNCTION__,
					flag,
					flag & SELECTION_LEFT	? "Left"	: "-",
					flag & SELECTION_TOP	? "Top"		: "-",
					flag & SELECTION_RIGHT	? "Right"	: "-",
					flag & SELECTION_BOTTOM	? "Bottom"	: "-"
					);
	*/

	if(!flag)
		return origin;
	else if((flag&0x8F) == SELECTION_ACTIVE)
		return lib3270_move_selected_area(h,origin,baddr);

	row = baddr/h->view.cols;
	col = baddr%h->view.cols;

	if(flag & SELECTION_LEFT)		// Update left margin
		origin = first = ((first/h->view.cols)*h->view.cols) + col;

	if(flag & SELECTION_TOP)		// Update top margin
		origin = first = (row*h->view.cols) + (first%h->view.cols);

	if(flag & SELECTION_RIGHT) 		// Update right margin
		origin = last = ((last/h->view.cols)*h->view.cols) + col;

	if(flag & SELECTION_BOTTOM)		// Update bottom margin
		origin = last = (row*h->view.cols) + (last%h->view.cols);

	trace("origin=%d first=%d last=%d",origin,first,last);

	if(first < last)
		do_select(h,first,last,h->rectsel);
	else
		do_select(h,last,first,h->rectsel);

	cursor_move(h,h->select.end);

	return origin;
}

LIB3270_EXPORT int lib3270_move_selection(H3270 *hSession, LIB3270_DIRECTION dir) {
	int start, end;

	if(!hSession->selected || hSession->select.start == hSession->select.end)
		return ENOENT;

	start = hSession->select.start;
	end   = hSession->select.end;

	switch(dir) {
	case LIB3270_DIR_UP:
		if(start <= ((int) hSession->view.cols))
			return EINVAL;
		start -= hSession->view.cols;
		end   -= hSession->view.cols;
		break;

	case LIB3270_DIR_DOWN:
		if(end >= ((int) (hSession->view.cols * (hSession->view.rows-1))))
			return EINVAL;
		start += hSession->view.cols;
		end   += hSession->view.cols;
		break;

	case LIB3270_DIR_LEFT:
		if( (start % hSession->view.cols) < 1)
			return EINVAL;
		start--;
		end--;
		break;

	case LIB3270_DIR_RIGHT:
		if( (end % hSession->view.cols) >= (hSession->view.cols-1))
			return EINVAL;
		start++;
		end++;
		break;

	default:
		return -1;
	}

	do_select(hSession,start,end,hSession->rectsel);
	cursor_move(hSession,hSession->select.end);

	return 0;
}