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. */
60 static 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
69 static void *
70 alloc_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 static void
82 fill_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 */
102 static size_t
103 row_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 */
122 static void
123 sel_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 */
190 void
191 mouse_sel_init()
192 {
193 memset(&sel, 0, sizeof(struct selection));
194 }
195
196 /*
197 * Starts a sel (when mouse is pressed).
198 */
199 void
200 mouse_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 */
217 void
218 mouse_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 */
243 void
244 mouse_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 */
263 void
264 mouse_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 */
275 void
276 mouse_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 */
289 void
290 mouse_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