selection.c revision 1.1
1/* $NetBSD: selection.c,v 1.1 2002/06/26 23:13:08 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.1 2002/06/26 23:13:08 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
81static void
82fill_buf(char *ptr, struct mouse *m, size_t row, size_t col, size_t end)
83{
84	struct wsdisplay_char ch;
85	ch.row = sel.start_row;
86	for (ch.col = col; ch.col < end; ch.col++) {
87		if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) {
88			warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
89			*ptr++ = ' ';
90		} else {
91			*ptr++ = ch.letter;
92		}
93	}
94}
95
96
97/*
98 * This function scans the specified line and checks its
99 * length. Characters at the end of it which match isspace() are not
100 * counted.
101 */
102static size_t
103row_length(struct mouse *m, size_t row)
104{
105	struct wsdisplay_char ch;
106
107	ch.col = m->max_col;
108	ch.row = row;
109	do {
110		if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1)
111			warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
112		ch.col--;
113	} while (isspace((unsigned char)ch.letter));
114	return ch.col + 2;
115}
116
117/*
118 * This (complex) function copies all the text englobed in the current
119 * sel to the sel buffer. It does space trimming at end of
120 * lines if it is selected.
121 */
122static void
123sel_copy_text(struct mouse *m)
124{
125	char *str, *ptr;
126	size_t r, l;
127
128	if (sel.start_row == sel.end_row) {
129		/* Selection is one row */
130		l = row_length(m, sel.start_row);
131		if (sel.start_col > l)
132			/* Selection is after last character,
133			 * therefore it is empty */
134			str = NULL;
135		else {
136			if (sel.end_col > l)
137				sel.end_col = l;
138			ptr = str = alloc_sel( sel.end_col - sel.start_col + 1);
139			if (ptr == NULL)
140				return;
141
142			fill_buf(ptr, m, sel.start_row, sel.start_col,
143			    sel.end_col);
144			*ptr = '\0';
145		}
146	} else {
147		/* Selection is multiple rows */
148		ptr = str =  alloc_sel(sel.abs_end - sel.abs_start + 1);
149		if (ptr == NULL)
150			return;
151
152		/* Calculate and copy first line */
153		l = row_length(m, sel.start_row);
154		if (sel.start_col < l) {
155			fill_buf(ptr, m, sel.start_row, sel.start_col, l);
156			*ptr++ = '\r';
157		}
158
159		/* Copy mid lines if there are any */
160		if ((sel.end_row - sel.start_row) > 1) {
161			for (r = sel.start_row + 1; r <= sel.end_row - 1; r++) {
162				fill_buf(ptr, m, r, 0, row_length(m, r));
163				*ptr++ = '\r';
164			}
165		}
166
167		/* Calculate and copy end line */
168		l = row_length(m, sel.end_row);
169		if (sel.end_col < l)
170			l = sel.end_col;
171		fill_buf(ptr, m, sel.end_row, 0, l);
172		*ptr = '\0';
173	}
174
175	if (sel.text != NULL) {
176		free(sel.text);
177		sel.text = NULL;
178	}
179
180	if (str != NULL) {
181		sel.text = str;
182		sel.text_size = ptr - str;
183	}
184}
185
186/*
187 * Initializes sel data. It should be called only once, at
188 * wsmoused startup to initialize pointers.
189 */
190void
191mouse_sel_init()
192{
193	memset(&sel, 0, sizeof(struct selection));
194}
195
196/*
197 * Starts a sel (when mouse is pressed).
198 */
199void
200mouse_sel_start(struct mouse *m)
201{
202	if (sel.text != NULL) {
203		free(sel.text);
204		sel.text = NULL;
205	}
206
207	sel.start_row = m->row;
208	sel.start_col = m->col;
209	mouse_sel_calculate(m);
210	m->selecting = 1;
211}
212
213/*
214 * Ends a sel. Text is copied to memory for future pasting and
215 * highlighted region is returned to normal state.
216 */
217void
218mouse_sel_end(struct mouse *m)
219{
220	size_t i;
221
222	mouse_sel_calculate(m);
223
224	/* Invert sel coordinates if needed */
225	if (sel.start_col > sel.end_col) {
226		i = sel.end_col;
227		sel.end_col = sel.start_col;
228		sel.start_col = i;
229	}
230	if (sel.start_row > sel.end_row) {
231		i = sel.end_row;
232		sel.end_row = sel.start_row;
233		sel.start_row = i;
234	}
235
236	sel_copy_text(m);
237	m->selecting = 0;
238}
239
240/*
241 * Calculates sel absolute postitions.
242 */
243void
244mouse_sel_calculate(struct mouse *m)
245{
246	size_t i = m->max_col + 1;
247
248	sel.end_row = m->row;
249	sel.end_col = m->col;
250	sel.abs_start = sel.start_row * i + sel.start_col;
251	sel.abs_end = sel.end_row * i + sel.end_col;
252
253	if (sel.abs_start > sel.abs_end) {
254		i = sel.abs_end;
255		sel.abs_end = sel.abs_start;
256		sel.abs_start = i;
257	}
258}
259
260/*
261 * Hides highlighted region, returning it to normal colors.
262 */
263void
264mouse_sel_hide(struct mouse *m)
265{
266	size_t i;
267
268	for (i = sel.abs_start; i <= sel.abs_end; i++)
269		char_invert(m, 0, i);
270}
271
272/*
273 * Highlights selected region.
274 */
275void
276mouse_sel_show(struct mouse *m)
277{
278	size_t i;
279
280	mouse_sel_calculate(m);
281	for (i = sel.abs_start; i <= sel.abs_end; i++)
282		char_invert(m, 0, i);
283}
284
285
286/*
287 * Pastes selected text into the active console.
288 */
289void
290mouse_sel_paste(struct mouse *m)
291{
292	size_t i;
293
294	if (sel.text == NULL)
295		return;
296	for (i = 0; i < sel.text_size; i++)
297		if (ioctl(m->tty_fd, TIOCSTI, &sel.text[i]) == -1)
298			warn("ioctl(TIOCSTI)");
299}
300