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