1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  *      Copyright (c) 1997, by Sun Microsystems, Inc.
28  *      All rights reserved.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.7	*/
32 
33 /*LINTLIBRARY*/
34 
35 #include <sys/types.h>
36 #include "utility.h"
37 
38 #define	Scrollable(f)		((f)->drows > (f)->rows || \
39 					(f)->dcols > (f)->cols)
40 #define	Connected(f)		((f) -> form != (FORM *) 0)
41 #define	OnPage(f)		((f) -> page == P((f) -> form))
42 #define	Posted(f)		(Status((f) -> form, POSTED))
43 #define	Visible(f)		(Opt(f, O_VISIBLE) && OnPage(f))
44 #define	isCurrent(f)		((f) == C((f) -> form))
45 #define	Justified(f)		(Just(f) != NO_JUSTIFICATION && \
46 				OneRow(f) && Opt(f, O_STATIC) && \
47 				f->dcols == f->cols)
48 
49 /* _data_beg - return ptr to first non-blank char in v[n] (v on failure) */
50 char *
51 _data_beg(char *v, int n)
52 {
53 	char *vend = v + n;
54 	while (v < vend && *v == ' ') ++v;
55 	return (v == vend ? v - n : v);
56 }
57 
58 /*
59  * _data_end - return ptr to char after last
60  * non-blank char in v[n] (v on failure)
61  */
62 char *
63 _data_end(char *v, int n)
64 {
65 	char *vend = v + n;
66 	while (vend > v && *(vend - 1) == ' ') --vend;
67 	return (vend);
68 }
69 
70 /* _whsp_beg - return ptr to first blank in v[n] (v on failure) */
71 char *
72 _whsp_beg(char *v, int n)
73 {
74 	char * vend = v + n;
75 	while (v < vend && *v != ' ') ++v;
76 	return (v == vend ? v - n : v);
77 }
78 
79 /* _whsp_end - return ptr to char after last blank in v[n] (v on failure) */
80 char *
81 _whsp_end(char *v, int n)
82 {
83 	char * vend = v + n;
84 	while (vend > v && *(vend - 1) != ' ') --vend;
85 	return (vend);
86 }
87 
88 /*
89  * _adjust_cursor - adjust cursor based on
90  * offset of v from beginning of field buffer
91  */
92 void
93 _adjust_cursor(FORM *f, char *v)
94 {
95 	int pos = (int) (v - Buf(C(f)));
96 
97 	Y(f) = pos / Xmax(f);
98 	X(f) = pos - Y(f) * Xmax(f);
99 
100 	if (Y(f) >= Ymax(f))
101 		Y(f) = 0;
102 }
103 
104 /*
105  * _buf_to_win - copy buffer to window(trailing
106  * blanks on each line are not copied)
107  */
108 void
109 _buf_to_win(FIELD *f, WINDOW *w)
110 {
111 	char *	v = Buf(f);
112 	int	xmax, ymax, y, n;
113 
114 	getmaxyx(w, ymax, xmax);
115 
116 	for (y = 0; y < ymax; ++y) {
117 		if ((n = (int) (_data_end(v, xmax) - v)) != 0) {
118 			(void) wmove(w, y, 0);
119 			(void) waddnstr(w, v, n);
120 		}
121 		v += xmax;
122 	}
123 }
124 
125 /* _win_to_buf - copy window to buffer */
126 void
127 _win_to_buf(WINDOW *w, FIELD *f)
128 {
129 	int		i;
130 	int		size	= BufSize(f);
131 	int		pad	= Pad(f);
132 	char *		v	= Buf(f);
133 
134 	(void) wmove(w, 0, 0);
135 	(void) winnstr(w, v, size);
136 
137 	if (pad != ' ')
138 		for (i = 0; i < size; ++i, ++v)
139 			if (*v == pad)
140 				*v = ' '; /* replace pad char with blank */
141 }
142 
143 /* _pos_form_cursor - move to cursor position and sync up cursor */
144 int
145 _pos_form_cursor(FORM *f)
146 {
147 	WINDOW *	w = W(f);
148 	FIELD *		c = C(f);
149 
150 	if (!w)
151 		return (E_SYSTEM_ERROR);
152 
153 	(void) wmove(w, Y(f), X(f));
154 
155 	if (Opt(c, O_PUBLIC)) {
156 		if (Scrollable(c)) {
157 			int row, col;
158 
159 			if (OneRow(c)) {
160 				row = c->frow;
161 				col = c->fcol + X(f) - B(f);
162 			} else {
163 				row = c -> frow + Y(f) - T(f);
164 				col = c -> fcol + X(f);
165 			}
166 
167 			(void) wmove(Sub(f), row, col);
168 			wcursyncup(Sub(f));
169 		} else
170 			wcursyncup(w);
171 	} else {
172 		(void) wmove(Sub(f), c -> frow, c -> fcol);
173 		wcursyncup(Sub(f));
174 	}
175 	return (E_OK);
176 }
177 
178 /* _update_current - sync up current field */
179 int
180 _update_current(FORM *f)
181 {
182 	WINDOW *	w = W(f);
183 	FIELD *		c = C(f);
184 
185 	if (!w)
186 		return (E_SYSTEM_ERROR);
187 
188 	if (Opt(c, O_PUBLIC)) {
189 		if (Scrollable(c)) {
190 			if (OneRow(c)) {
191 				int xmax = B(f) + c->cols;
192 
193 				if (X(f) < B(f))
194 					B(f) = X(f);
195 				else if (X(f) >= xmax)
196 					B(f) = X(f) - c->cols + 1;
197 
198 				(void) copywin(w, Sub(f), 0, B(f), c->frow,
199 				    c->fcol, c->frow, c->fcol + c->cols - 1,
200 				    FALSE);
201 
202 			} else {
203 				int	ymax = T(f) + c -> rows;
204 				int	ys, ye;
205 
206 				if (Y(f) < T(f)) {
207 					T(f) = Y(f);
208 					Set(c, TOP_CHG);
209 				}
210 				if (Y(f) >= ymax) {
211 					T(f) = Y(f) - c -> rows + 1;
212 					Set(c, TOP_CHG);
213 				}
214 				if (Status(c, TOP_CHG)) {
215 					ys = T(f);
216 					ye = ys + c -> rows;
217 					Clr(c, TOP_CHG);
218 				} else {
219 	/* intersect changed lines with visible lines */
220 					for (ys = T(f); ys < ymax; ++ys)
221 						if (is_linetouched(w, ys))
222 							break;
223 
224 					for (ye = ys; ye < ymax; ++ye)
225 						if (! is_linetouched(w, ye))
226 							break;
227 				}
228 				if (ye - ys) {
229 					(void) copywin(w, Sub(f), ys, 0,
230 					    c -> frow + ys - T(f), c -> fcol,
231 					    c -> frow + ye - T(f) - 1,
232 					    c -> fcol + c -> cols - 1, FALSE);
233 				}
234 			}
235 			wsyncup(Sub(f));
236 		} else
237 			wsyncup(w);
238 	}
239 	(void) untouchwin(w);
240 	return (_pos_form_cursor(f));
241 }
242 
243 /* justify - justify field f in window w as given by Just(f) */
244 static void
245 justify(FIELD *f, WINDOW *w)
246 {
247 	char *	v	= _data_beg(Buf(f), BufSize(f));
248 	char *	vend	= _data_end(Buf(f), BufSize(f));
249 	int	n	= (int) (vend - v);
250 	int	x	= 0;
251 
252 	if (n) {
253 		switch (Just(f)) {
254 			case JUSTIFY_LEFT:
255 				break;
256 			case JUSTIFY_CENTER:
257 				x = (f -> cols - n) / 2;
258 				break;
259 			case JUSTIFY_RIGHT:
260 				x = f -> cols - n;
261 				break;
262 		}
263 		(void) wmove(w, 0, x);
264 		(void) waddnstr(w, v, n);
265 	}
266 }
267 
268 /* unjustify - left justify field f in window w for editing */
269 static void
270 unjustify(FIELD *f, WINDOW *w)
271 {
272 	char *	v	= _data_beg(Buf(f), BufSize(f));
273 	char *	vend	= _data_end(Buf(f), BufSize(f));
274 	int	n	= (int) (vend - v);
275 
276 	if (n) {
277 		(void) wmove(w, 0, 0);
278 		(void) waddnstr(w, v, n);
279 	}
280 }
281 
282 /* _sync_buffer - sync current field with characters in window */
283 void
284 _sync_buffer(FORM *f)
285 {
286 	if (Status(f, WIN_CHG)) {
287 		Clr(f, WIN_CHG);
288 		Set(f, BUF_CHG);
289 		_win_to_buf(W(f), C(f));
290 		(void) wmove(W(f), Y(f), X(f));
291 	}
292 }
293 
294 /* _sync_linked - sync fields linked to field f */
295 int
296 _sync_linked(FIELD *f)
297 {
298 	FIELD *		p = f -> link;
299 	int		err = 0;
300 
301 	while (p != f) {
302 		if (_sync_field(p) != E_OK)
303 			++err;
304 		p = p -> link;
305 	}
306 	return (err ? E_SYSTEM_ERROR : E_OK);
307 }
308 
309 /* display_field - display field f */
310 static int
311 display_field(FIELD *f)
312 {
313 	WINDOW *	w = derwin(Sub(f -> form), f -> rows, f -> cols,
314 			    f -> frow, f -> fcol);
315 
316 	if (!w)
317 		return (FALSE);
318 
319 	wbkgdset(w, Pad(f) | Back(f));
320 	(void) wattrset(w, Fore(f));
321 	(void) werase(w);
322 
323 	if (Opt(f, O_PUBLIC)) {
324 		if (Justified(f))
325 			justify(f, w);
326 		else
327 			_buf_to_win(f, w);
328 	}
329 	wsyncup(w);
330 	(void) delwin(w);
331 	Clr(f, TOP_CHG);
332 	return (TRUE);
333 }
334 
335 /* erase_field - erase field f */
336 static int
337 erase_field(FIELD *f)
338 {
339 	WINDOW *	w = derwin(Sub(f -> form), f -> rows, f -> cols,
340 			    f -> frow, f -> fcol);
341 
342 	if (!w)
343 		return (FALSE);
344 
345 	(void) werase(w);
346 	wsyncup(w);
347 	(void) delwin(w);
348 	return (TRUE);
349 }
350 
351 /* _sync_field - sync the field after a change to the field buffer */
352 int
353 _sync_field(FIELD *f)
354 {
355 	int v = TRUE;
356 
357 	if (Connected(f) && Posted(f) && Visible(f)) {
358 		if (isCurrent(f)) {
359 			FORM *		p = f -> form;
360 			WINDOW *	w = W(p);
361 
362 			Clr(p, WIN_CHG | BUF_CHG);
363 
364 			Y(p) = 0;
365 			X(p) = 0;
366 			T(p) = 0;
367 			B(p) = 0;
368 			(void) werase(w);
369 
370 			if (Opt(f, O_PUBLIC) && Justified(f))
371 				unjustify(f, w);
372 			else
373 				_buf_to_win(f, w);
374 
375 			Set(f, TOP_CHG);
376 			(void) _update_current(p);
377 		} else
378 			v = display_field(f);
379 	}
380 	Set(f, USR_CHG);
381 
382 	return (v ? E_OK : E_SYSTEM_ERROR);
383 }
384 
385 /* _sync_attrs - sync the field after a change to a field attribute */
386 int
387 _sync_attrs(FIELD *f)
388 {
389 	int v = TRUE;
390 
391 	if (Connected(f) && Posted(f) && Visible(f)) {
392 		if (isCurrent(f)) {
393 			FORM *		p = f -> form;
394 			WINDOW *	w = W(p);
395 
396 			_sync_buffer(p);
397 
398 			wbkgdset(w, Pad(f) | Back(f));
399 			(void) wattrset(w, Fore(f));
400 			(void) werase(w);
401 
402 			if (Opt(f, O_PUBLIC)) {
403 				if (Justified(f))
404 					unjustify(f, w);
405 				else
406 					_buf_to_win(f, w);
407 			} else {
408 				(void) copywin(w, Sub(p), 0, 0, f -> frow,
409 				    f -> fcol, f -> rows - 1, f -> cols - 1,
410 				    FALSE);
411 				wsyncup(Sub(p));
412 				_buf_to_win(f, w);
413 			}
414 			Set(f, TOP_CHG);
415 			(void) _update_current(p);
416 		} else
417 			v = display_field(f);
418 	}
419 	return (v ? E_OK : E_SYSTEM_ERROR);
420 }
421 
422 int
423 _sync_opts(FIELD *f, OPTIONS opts)
424 {
425 	int		v = TRUE;
426 	OPTIONS		oldopts = f -> opts;
427 	OPTIONS x = opts ^ oldopts;
428 	/* x & opt indicates option opt has changed state */
429 	f -> opts = opts;
430 
431 	if (Connected(f)) {
432 		if (isCurrent(f)) {
433 			f -> opts = oldopts;
434 			return (E_CURRENT);
435 		}
436 		if (Posted(f) && OnPage(f)) {
437 			if (x & O_VISIBLE) {
438 				if (Opt(f, O_VISIBLE))
439 					v = display_field(f);
440 				else
441 					v = erase_field(f);
442 			} else if (x & O_PUBLIC) {
443 				if (Opt(f, O_VISIBLE))
444 					v = display_field(f);
445 			}
446 		}
447 	}
448 
449 	if (x & O_STATIC) {
450 		BOOLEAN	onerow = OneRow(f);
451 		int		max = f->maxgrow;
452 
453 		if (Opt(f, O_STATIC)) {	/* growth being turned off */
454 			Clr(f, GROWABLE);
455 
456 			if (onerow && f->cols == f->dcols &&
457 			    Just(f) != NO_JUSTIFICATION && Posted(f) &&
458 			    OnPage(f) && Opt(f, O_VISIBLE)) {
459 				(void) display_field(f);
460 			}
461 		} else if (!max || (onerow && f->dcols < max) ||
462 			(!onerow && f->drows < max)) {
463 			Set(f, GROWABLE);
464 
465 			if (onerow && Just(f) != NO_JUSTIFICATION &&
466 			    Posted(f) && OnPage(f) && Opt(f, O_VISIBLE)) {
467 				(void) display_field(f);
468 			}
469 		}
470 	}
471 
472 	return (v ? E_OK : E_SYSTEM_ERROR);
473 }
474 
475 /* _validate - validate current field */
476 int
477 _validate(FORM *f)
478 {
479 	FIELD * c = C(f);
480 
481 	_sync_buffer(f);
482 
483 	if (Status(f, BUF_CHG) || !Opt(c, O_PASSOK)) {
484 		if (CheckField(c)) {
485 			Clr(f, BUF_CHG);
486 			Set(c, USR_CHG);
487 			(void) _sync_linked(c);
488 		} else
489 			return (FALSE);
490 	}
491 	return (TRUE);
492 }
493 
494 /*
495  * _set_current_field - change current field on form f to given field.
496  * POSTED flag is set unless this is called from post_form().
497  */
498 int
499 _set_current_field(FORM *f, FIELD *field)
500 {
501 	WINDOW *	w = W(f);
502 	FIELD *		c = C(f);
503 
504 	if (c != field || ! Status(f, POSTED)) {
505 		if (w) {
506 			if (Visible(c)) {
507 				(void) _update_current(f);
508 
509 				if (Opt(c, O_PUBLIC)) {
510 					if (Scrollable(c)) {
511 						if (T(f) == 0)
512 							Clr(c, TOP_CHG);
513 						else
514 							Set(c, TOP_CHG);
515 					} else if (Justified(c)) {
516 						(void) werase(w);
517 						justify(c, w);
518 						wsyncup(w);
519 					}
520 				}
521 			}
522 			(void) delwin(w);
523 		}
524 		c = field;
525 
526 		if (!Opt(c, O_PUBLIC) || Scrollable(c))
527 			w = newwin(c -> drows, c -> dcols, 0, 0);
528 		else
529 			w = derwin(Sub(f), c -> rows, c -> cols, c -> frow,
530 			    c -> fcol);
531 
532 		if (!w)
533 			return (E_SYSTEM_ERROR);
534 
535 		C(f) = c;
536 		W(f) = w;
537 		wbkgdset(w, Pad(c) | Back(c));
538 		(void) wattrset(w, Fore(c));
539 
540 		if (!Opt(c, O_PUBLIC) || Scrollable(c)) {
541 			(void) werase(w);
542 			_buf_to_win(c, w);
543 		} else if (Justified(c)) {
544 			(void) werase(w);
545 			unjustify(c, w);
546 			wsyncup(w);
547 		}
548 		(void) untouchwin(w);
549 		Clr(f, WIN_CHG | BUF_CHG);
550 	}
551 	Y(f) = 0;
552 	X(f) = 0;
553 	T(f) = 0;
554 	B(f) = 0;
555 	return (E_OK);
556 }
557 
558 /*
559  * _set_form_page - display given page and set current field to c.
560  * if c is null, then set current field to first field on page.
561  * POSTED flag is set unless this is called from post_form().
562  */
563 int
564 _set_form_page(FORM *f, int page, FIELD *c)
565 {
566 	if (P(f) != page || ! Status(f, POSTED)) {
567 		FIELD * x = f -> field [Smin(f, page)];
568 		FIELD * p = x;
569 
570 		(void) werase(Sub(f));
571 		P(f) = page;
572 
573 		do {
574 			if (Opt(p, O_VISIBLE))
575 				if (!display_field(p))
576 					return (E_SYSTEM_ERROR);
577 			p = p -> snext;
578 
579 		} while (p != x);
580 
581 		return (c ? _set_current_field(f, c) : _first_field(f));
582 	}
583 	return (E_OK);
584 }
585