selection.c revision 1.2
1/* $NetBSD: selection.c,v 1.2 2002/07/04 20:50:29 christos Exp $ */
2
3/*
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julio Merino.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. The name authors may not be used to endorse or promote products
16 *    derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33
34#ifndef lint
35__RCSID("$NetBSD: selection.c,v 1.2 2002/07/04 20:50:29 christos Exp $");
36#endif /* not lint */
37
38#include <sys/ioctl.h>
39#include <sys/time.h>
40#include <sys/types.h>
41#include <sys/tty.h>
42#include <dev/wscons/wsconsio.h>
43
44#include <ctype.h>
45#include <fcntl.h>
46#include <err.h>
47#include <errno.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52
53#include "pathnames.h"
54#include "wsmoused.h"
55
56/* This struct holds information about a sel. For now there is
57 * only one global instace, but using a structre gives us a place for
58 * maintaining all the variables together. Also, someone may want to
59 * allow multiple sels, so it is easier this way. */
60static struct selection {
61	size_t start_row, start_col;
62	size_t end_row, end_col;
63	size_t abs_start, abs_end;
64	size_t text_size;
65	char *text;
66} sel;
67
68
69static void *
70alloc_sel(size_t len)
71{
72	void *ptr;
73	if ((ptr = malloc(len)) == NULL) {
74		warn("Cannot allocate memory for sel %lu",
75		    (unsigned long)len);
76		return NULL;
77	}
78	return ptr;
79}
80
81/*
82 * Copies a region of a line inside the buffer pointed by ptr. We use
83 * a double pointer because we modify the pointer. When the function
84 * finishes, ptr points to the end of the buffer.
85 */
86static char *
87fill_buf(char *ptr, struct mouse *m, size_t row, size_t col, size_t end)
88{
89	struct wsdisplay_char ch;
90	ch.row = row;
91	for (ch.col = col; ch.col < end; ch.col++) {
92		if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) {
93			warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
94			*ptr++ = ' ';
95		} else {
96			*ptr++ = ch.letter;
97		}
98	}
99	return ptr;
100}
101
102
103/*
104 * This function scans the specified line and checks its
105 * length. Characters at the end of it which match isspace() are not
106 * counted.
107 */
108static size_t
109row_length(struct mouse *m, size_t row)
110{
111	struct wsdisplay_char ch;
112
113	ch.col = m->max_col;
114	ch.row = row;
115	do {
116		if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1)
117			warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
118		ch.col--;
119	} while (isspace((unsigned char)ch.letter));
120	return ch.col + 2;
121}
122
123/*
124 * This (complex) function copies all the text englobed in the current
125 * sel to the sel buffer. It does space trimming at end of
126 * lines if it is selected.
127 */
128static void
129sel_copy_text(struct mouse *m)
130{
131	char *str, *ptr;
132	size_t r, l;
133
134	if (sel.start_row == sel.end_row) {
135		/* Selection is one row */
136		l = row_length(m, sel.start_row);
137		if (sel.start_col > l)
138			/* Selection is after last character,
139			 * therefore it is empty */
140			str = NULL;
141		else {
142			if (sel.end_col > l)
143				sel.end_col = l;
144			ptr = str = alloc_sel(sel.end_col - sel.start_col + 1);
145			if (ptr == NULL)
146				return;
147
148			ptr = fill_buf(ptr, m, sel.start_row, sel.start_col,
149			    sel.end_col);
150			*ptr = '\0';
151		}
152	} else {
153		/* Selection is multiple rows */
154		ptr = str = alloc_sel(sel.abs_end - sel.abs_start + 1);
155		if (ptr == NULL)
156			return;
157
158		/* Calculate and copy first line */
159		l = row_length(m, sel.start_row);
160		if (sel.start_col < l) {
161			ptr = fill_buf(ptr, m, sel.start_row, sel.start_col, l);
162			*ptr++ = '\r';
163		}
164
165		/* Copy mid lines if there are any */
166		if ((sel.end_row - sel.start_row) > 1) {
167			for (r = sel.start_row + 1; r <= sel.end_row - 1; r++) {
168				ptr = fill_buf(ptr, m, r, 0, row_length(m, r));
169				*ptr++ = '\r';
170			}
171		}
172
173		/* Calculate and copy end line */
174		l = row_length(m, sel.end_row);
175		if (sel.end_col < l)
176			l = sel.end_col;
177		ptr = fill_buf(ptr, m, sel.end_row, 0, l);
178		*ptr = '\0';
179	}
180
181	if (sel.text != NULL) {
182		free(sel.text);
183		sel.text = NULL;
184	}
185
186	if (str != NULL) {
187		sel.text = str;
188		sel.text_size = ptr - str;
189	}
190}
191
192/*
193 * Initializes sel data. It should be called only once, at
194 * wsmoused startup to initialize pointers.
195 */
196void
197mouse_sel_init()
198{
199	memset(&sel, 0, sizeof(struct selection));
200}
201
202/*
203 * Starts a sel (when mouse is pressed).
204 */
205void
206mouse_sel_start(struct mouse *m)
207{
208	if (sel.text != NULL) {
209		free(sel.text);
210		sel.text = NULL;
211	}
212
213	sel.start_row = m->row;
214	sel.start_col = m->col;
215	mouse_sel_calculate(m);
216	m->selecting = 1;
217}
218
219/*
220 * Ends a sel. Text is copied to memory for future pasting and
221 * highlighted region is returned to normal state.
222 */
223void
224mouse_sel_end(struct mouse *m)
225{
226	size_t i;
227
228	mouse_sel_calculate(m);
229
230	/* Invert sel coordinates if needed */
231	if (sel.start_col > sel.end_col) {
232		i = sel.end_col;
233		sel.end_col = sel.start_col;
234		sel.start_col = i;
235	}
236	if (sel.start_row > sel.end_row) {
237		i = sel.end_row;
238		sel.end_row = sel.start_row;
239		sel.start_row = i;
240	}
241
242	sel_copy_text(m);
243	m->selecting = 0;
244}
245
246/*
247 * Calculates sel absolute postitions.
248 */
249void
250mouse_sel_calculate(struct mouse *m)
251{
252	size_t i = m->max_col + 1;
253
254	sel.end_row = m->row;
255	sel.end_col = m->col;
256	sel.abs_start = sel.start_row * i + sel.start_col;
257	sel.abs_end = sel.end_row * i + sel.end_col;
258
259	if (sel.abs_start > sel.abs_end) {
260		i = sel.abs_end;
261		sel.abs_end = sel.abs_start;
262		sel.abs_start = i;
263	}
264}
265
266/*
267 * Hides highlighted region, returning it to normal colors.
268 */
269void
270mouse_sel_hide(struct mouse *m)
271{
272	size_t i;
273
274	for (i = sel.abs_start; i <= sel.abs_end; i++)
275		char_invert(m, 0, i);
276}
277
278/*
279 * Highlights selected region.
280 */
281void
282mouse_sel_show(struct mouse *m)
283{
284	size_t i;
285
286	mouse_sel_calculate(m);
287	for (i = sel.abs_start; i <= sel.abs_end; i++)
288		char_invert(m, 0, i);
289}
290
291
292/*
293 * Pastes selected text into the active console.
294 */
295void
296mouse_sel_paste(struct mouse *m)
297{
298	size_t i;
299
300	if (sel.text == NULL)
301		return;
302	for (i = 0; i < sel.text_size; i++)
303		if (ioctl(m->tty_fd, TIOCSTI, &sel.text[i]) == -1)
304			warn("ioctl(TIOCSTI)");
305}
306