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