uifc32.c 71 KB
Newer Older
deuce's avatar
deuce committed
1
/* uifc32.c */
2
3
4

/* Curses implementation of UIFC (user interface) library based on uifc.c */

rswindell's avatar
rswindell committed
5
6
/* $Id$ */

7
8
9
10
/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html		*
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#ifdef __unix__
	#include <stdio.h>
	#include <unistd.h>
	#ifdef __QNX__
		#include <strings.h>
	#endif
    #define mswait(x) delay(x)
#elif defined(_WIN32)
	#include <share.h>
	#include <windows.h>
deuce's avatar
deuce committed
48
	#include <malloc.h>
49
50
51
	#define mswait(x) Sleep(x)
#endif

52
#include "ciolib.h"
53
#include "keys.h"
54
#include "uifc.h"
55
#define MAX_GETSTR	5120
56
57
58
59
60
61

#define BLINK	128

static int   cursor;
static char* helpfile=0;
static uint  helpline=0;
deuce's avatar
deuce committed
62
63
64
static size_t blk_scrn_len;
static char* blk_scrn;
static uchar* tmp_buffer;
65
static uchar* tmp_buffer2;
66
67
68
69
static win_t sav[MAX_BUFS];
static uifcapi_t* api;

/* Prototypes */
70
static int   uprintf(int x, int y, unsigned attr, char *fmt,...);
71
72
static void  bottomline(int line);
static char  *utimestr(time_t *intime);
deuce's avatar
deuce committed
73
static void  help(void);
74
static int   ugetstr(int left, int top, int width, char *outstr, int max, long mode, int *lastkey);
75
static void  timedisplay(BOOL force);
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

/* API routines */
static void uifcbail(void);
static int  uscrn(char *str);
static int  ulist(int mode, int left, int top, int width, int *dflt, int *bar
	,char *title, char **option);
static int  uinput(int imode, int left, int top, char *prompt, char *str
	,int len ,int kmode);
static void umsg(char *str);
static void upop(char *str);
static void sethelp(int line, char* file);
static void showbuf(int mode, int left, int top, int width, int height, char *title
	, char *hbuf, int *curp, int *barp);

/* Dynamic menu support */
static int *last_menu_cur=NULL;
static int *last_menu_bar=NULL;
static int save_menu_cur=-1;
static int save_menu_bar=-1;
95
static int save_menu_opts=-1;
96
97
98
99
100
101

static void reset_dynamic(void) {
	last_menu_cur=NULL;
	last_menu_bar=NULL;
	save_menu_cur=-1;
	save_menu_bar=-1;
102
	save_menu_opts=-1;
103
104
105
106
107
108
109
}

/****************************************************************************/
/* Initialization function, see uifc.h for details.							*/
/* Returns 0 on success.													*/
/****************************************************************************/

110
111
void uifc_mouse_enable(void)
{
112
113
114
115
116
	ciomouse_setevents(0);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END);
	ciomouse_addevent(CIOLIB_BUTTON_1_CLICK);
deuce's avatar
deuce committed
117
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
118
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
119
120
121
122
123
124
125
126
127
	showmouse();
}

void uifc_mouse_disable(void)
{
	ciomouse_setevents(0);
	hidemouse();
}

deuce's avatar
deuce committed
128
int kbwait(void) {
129
	int timeout=0;
130
	while(timeout++<50) {
131
		if(kbhit())
132
			return(TRUE);
133
		mswait(1);
134
135
136
137
	}
	return(FALSE);
}

138
int inkey(void)
deuce's avatar
deuce committed
139
140
141
142
{
	int c;

	c=getch();
143
144
	if(!c || c==0xff)
		c|=(getch()<<8);
deuce's avatar
deuce committed
145
146
	return(c);
}
147

148
149
int uifcini32(uifcapi_t* uifcapi)
{
150
	unsigned	i;
151
152
153
154
155
156
157
	struct	text_info txtinfo;

    if(uifcapi==NULL || uifcapi->size!=sizeof(uifcapi_t))
        return(-1);

    api=uifcapi;

158
    /* install function handlers */
159
160
161
162
163
164
165
166
    api->bail=uifcbail;
    api->scrn=uscrn;
    api->msg=umsg;
    api->pop=upop;
    api->list=ulist;
    api->input=uinput;
    api->sethelp=sethelp;
    api->showhelp=help;
deuce's avatar
deuce committed
167
	api->showbuf=showbuf;
168
	api->timedisplay=timedisplay;
169
	api->bottomline=bottomline;
170
	api->getstrxy=ugetstr;
171
	api->printf=uprintf;
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

    if(api->scrn_len!=0) {
        switch(api->scrn_len) {
            case 14:
                textmode(C80X14);
                break;
            case 21:
                textmode(C80X21);
                break;
            case 25:
                textmode(C80);
                break;
            case 28:
                textmode(C80X28);
                break;
            case 43:
                textmode(C80X43);
                break;
            case 50:
                textmode(C80X50);
                break;
            case 60:
                textmode(C80X60);
                break;
            default:
                textmode(C4350);
                break;
        }
    }

202
#if 0
203
    clrscr();
204
#endif
205
206
207
208

    gettextinfo(&txtinfo);
    /* unsupported mode? */
    if(txtinfo.screenheight<MIN_LINES
deuce's avatar
deuce committed
209
/*        || txtinfo.screenheight>MAX_LINES */
deuce's avatar
deuce committed
210
        || txtinfo.screenwidth<40) {
211
212
213
        textmode(C80);  /* set mode to 80x25*/
        gettextinfo(&txtinfo);
    }
214
	window(1,1,txtinfo.screenwidth,txtinfo.screenheight);
215
216

    api->scrn_len=txtinfo.screenheight;
deuce's avatar
deuce committed
217
218
219
    if(api->scrn_len<MIN_LINES) {
        cprintf("\7UIFC: Screen length (%u) must be %d lines or greater\r\n"
            ,api->scrn_len,MIN_LINES);
220
221
222
223
        return(-2);
    }
    api->scrn_len--; /* account for status line */

224
225
    if(txtinfo.screenwidth<40) {
        cprintf("\7UIFC: Screen width (%u) must be at least 40 characters\r\n"
226
227
228
229
230
231
232
            ,txtinfo.screenwidth);
        return(-3);
    }
	api->scrn_width=txtinfo.screenwidth;

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
233
234
235
236
237
238
            || txtinfo.currmode==MONO || txtinfo.currmode==BW40 || txtinfo.currmode==BW80
            || txtinfo.currmode==MONO14 || txtinfo.currmode==BW40X14 || txtinfo.currmode==BW80X14
            || txtinfo.currmode==MONO21 || txtinfo.currmode==BW40X21 || txtinfo.currmode==BW80X21
            || txtinfo.currmode==MONO28 || txtinfo.currmode==BW40X28 || txtinfo.currmode==BW80X28
            || txtinfo.currmode==MONO43 || txtinfo.currmode==BW40X43 || txtinfo.currmode==BW80X43
            || txtinfo.currmode==MONO50 || txtinfo.currmode==BW40X50 || txtinfo.currmode==BW80X50
deuce's avatar
deuce committed
239
240
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
			|| txtinfo.currmode==ATARI_40X24))
241
	{
242
243
244
245
246
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
247
    } else {
248
249
250
251
252
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
253
    }
254

deuce's avatar
deuce committed
255
	blk_scrn_len=api->scrn_width*api->scrn_len*2;
deuce's avatar
deuce committed
256
	if((blk_scrn=(char *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
257
258
259
260
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
261
	if((tmp_buffer=(uchar *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
262
263
264
265
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
266
	if((tmp_buffer2=(uchar *)malloc(blk_scrn_len))==NULL)  {
267
268
269
270
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
271
    for(i=0;i<blk_scrn_len;i+=2) {
272
        blk_scrn[i]='';
273
        blk_scrn[i+1]=api->cclr|(api->bclr<<4);
274
275
276
277
278
    }

    cursor=_NOCURSOR;
    _setcursortype(cursor);

279
	if(cio_api.mouse) {
280
		api->mode|=UIFC_MOUSE;
281
282
		uifc_mouse_enable();
	}
283

284
285
286
287
288
289
290
	/* A esc_delay of less than 10 is stupid... silently override */
	if(api->esc_delay < 10)
		api->esc_delay=25;

	if(cio_api.ESCDELAY)
		*(cio_api.ESCDELAY)=api->esc_delay;

291
292
	for(i=0; i<MAX_BUFS; i++)
		sav[i].buf=NULL;
deuce's avatar
deuce committed
293
294
295
	api->savnum=0;

	api->initialized=TRUE;
296

297
    return(0);
298
299
}

300
301
302
303
304
305
306
307
308
309
310
311
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
	unsigned char *screen;
	unsigned char *sbuffer;
	int sbufsize=0;
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;

	sbufsize=api->scrn_width*2*(api->scrn_len+1);
312
313
	screen=(unsigned char*)alloca(sbufsize);
	sbuffer=(unsigned char*)alloca(sbufsize);
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
	gettext(1,1,api->scrn_width,api->scrn_len+1,screen);
	while(1) {
		key=getch();
		if(key==0 || key==0xff)
			key|=getch()<<8;
		switch(key) {
			case CIO_KEY_MOUSE:
				getmouse(&mevent);
				if(mevent.startx<mevent.endx) {
					startx=mevent.startx;
					endx=mevent.endx;
				}
				else {
					startx=mevent.endx;
					endx=mevent.startx;
				}
				if(mevent.starty<mevent.endy) {
					starty=mevent.starty;
					endy=mevent.endy;
				}
				else {
					starty=mevent.endy;
					endy=mevent.starty;
				}
				switch(mevent.event) {
					case CIOLIB_BUTTON_1_DRAG_MOVE:
						memcpy(sbuffer,screen,sbufsize);
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
								if((sbuffer[pos*2+1]&0x70)!=0x10)
deuce's avatar
deuce committed
345
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x10;
346
								else
deuce's avatar
deuce committed
347
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x60;
348
349
350
351
352
353
354
355
356
								if(((sbuffer[pos*2+1]&0x70)>>4) == (sbuffer[pos*2+1]&0x0F)) {
									sbuffer[pos*2+1]|=0x08;
								}
							}
						}
						puttext(1,1,api->scrn_width,api->scrn_len+1,sbuffer);
						break;
					case CIOLIB_BUTTON_1_DRAG_END:
						lines=abs(mevent.endy-mevent.starty)+1;
357
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
358
359
360
361
362
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								copybuf[outpos++]=screen[(y*api->scrn_width+x)*2];
							}
363
364
365
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
							copybuf[outpos++]='\n';
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
						puttext(1,1,api->scrn_width,api->scrn_len+1,screen);
						return;
				}
				break;
			default:
				puttext(1,1,api->scrn_width,api->scrn_len+1,screen);
				ungetch(key);
				return;
		}
	}
}

382
383
384
385
386
387
388
389
390
static int uifc_getmouse(struct mouse_event *mevent)
{
	mevent->startx=0;
	mevent->starty=0;
	mevent->event=0;
	if(api->mode&UIFC_MOUSE) {
		getmouse(mevent);
		if(mevent->event==CIOLIB_BUTTON_3_CLICK)
			return(ESC);
391
392
393
394
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
		if(mevent->starty==api->buttony) {
			if(mevent->startx>=api->exitstart
					&& mevent->startx<=api->exitend
					&& mevent->event==CIOLIB_BUTTON_1_CLICK) {
				return(ESC);
			}
			if(mevent->startx>=api->helpstart
					&& mevent->startx<=api->helpend
					&& mevent->event==CIOLIB_BUTTON_1_CLICK) {
				return(CIO_KEY_F(1));
			}
		}
		return(0);
	}
	return(-1);
}

412
413
void uifcbail(void)
{
deuce's avatar
deuce committed
414
415
	int i;

416
417
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
418
	uifc_mouse_disable();
419
	suspendciolib();
deuce's avatar
deuce committed
420
421
422
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
423
	api->initialized=FALSE;
deuce's avatar
deuce committed
424
425
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
426
427
428
429
430
431
432
433
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
434
    textattr(api->bclr|(api->cclr<<4));
435
    gotoxy(1,1);
436
    clreol();
437
438
439
440
441
    gotoxy(3,1);
	cputs(str);
    if(!puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn))
        return(-1);
    gotoxy(1,api->scrn_len+1);
442
    clreol();
443
	reset_dynamic();
444
	setname(str);
445
446
447
448
449
450
451
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
452
	gettext(x1,y1,x2,y2,tmp_buffer2);
453
	if(down)
454
		puttext(x1,y1+1,x2,y2,tmp_buffer2);
455
	else
456
		puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)*2));
457
458
459
460
461
462
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
463
static void timedisplay(BOOL force)
464
465
466
467
468
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
469
	if(force || difftime(now,savetime)>=60) {
470
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
471
472
473
474
475
476
477
478
479
480
481
482
		savetime=now; 
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
static void truncsp(char *str)
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
483
	while(c && (uchar)str[c-1]<=' ') c--;
484
485
486
487
488
489
490
491
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

/****************************************************************************/
/* General menu function, see uifc.h for details.							*/
/****************************************************************************/
int ulist(int mode, int left, int top, int width, int *cur, int *bar
deuce's avatar
deuce committed
492
	, char *initial_title, char **option)
493
{
494
	uchar line[256],shade[256],*ptr
495
		,search[MAX_OPLN],bline=0,*win;
496
497
498
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
499
500
501
502
503
504
505
506
507
508
	int s_top=SCRN_TOP;
	int s_left=SCRN_LEFT;
	int s_right=SCRN_RIGHT;
	int s_bottom=api->scrn_len-3;
	int hbrdrsize=2;
	int lbrdrwidth=1;
	int rbrdrwidth=1;
	int vbrdrsize=4;
	int tbrdrwidth=3;
	int bbrdrwidth=1;
509
	int title_len;
510
	struct mouse_event mevnt;
511
	char	*title=NULL;
512
	int	a,b,c,longopt;
513
	int	optheight=0;
514
	int gotkey;
515
516
517
518
519
520
521
522
523
524
525
526
527
528
	uchar	hclr,lclr,bclr,cclr,lbclr;

	hclr=api->hclr;
	lclr=api->lclr;
	bclr=api->bclr;
	cclr=api->cclr;
	lbclr=api->lbclr;
	if(mode & WIN_INACT) {
		bclr=api->cclr;
		hclr=api->lclr;
		lclr=api->lclr;
		cclr=api->cclr;
		lbclr=(api->cclr<<4)|api->hclr;
	}
529
530
	title=strdup(initial_title==NULL?"":initial_title);

531
	uifc_mouse_disable();
532
533

	title_len=strlen(title);
534
535
536

	if(mode&WIN_FAT) {
		s_top=1;
537
		s_left=2;
538
539
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
540
	}
541
542
543
544
545
546
547
548
549
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}

550
551
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
552
	if(api->helpbuf!=NULL || api->helpixbfile[0]!=0) bline|=BL_HELP;
553
554
555
556
	if(mode&WIN_INS) bline|=BL_INS;
	if(mode&WIN_DEL) bline|=BL_DEL;
	if(mode&WIN_GET) bline|=BL_GET;
	if(mode&WIN_PUT) bline|=BL_PUT;
557
	if(mode&WIN_EDIT) bline|=BL_EDIT;
558
559
	if(api->bottomline != NULL)
		api->bottomline(bline);
560
	while(option!=NULL && opts<MAX_OPTS)
561
		if(option[opts]==NULL || option[opts][0]==0)
562
563
			break;
		else opts++;
deuce's avatar
deuce committed
564
	if(mode&WIN_XTR && opts<MAX_OPTS)
565
		opts++;
566
	optheight=opts+vbrdrsize;
567
	height=optheight;
568
569
570
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
571
572
	if(top+height>s_bottom)
		height=(s_bottom)-top;
573
574
	if(optheight>height)
		optheight=height;
575
576
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
577
		for(i=0;i<opts;i++) {
578
579
580
581
582
			if(option[i]!=NULL) {
				truncsp(option[i]);
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
583
584
		}
	}
585
586
587
588
589
590
591
	/* Determine minimum widths here to accomodate mouse "icons" in border */
	if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) {
		if(bline&BL_HELP && width<8)
			width=8;
		else if(width<5)
			width=5;
	}
592
593
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
594
595
596
597
598
		if(title_len>(width-hbrdrsize-2)) {
			*(title+width-hbrdrsize-2-3)='.';
			*(title+width-hbrdrsize-2-2)='.';
			*(title+width-hbrdrsize-2-1)='.';
			*(title+width-hbrdrsize-2)=0;
599
			title_len=strlen(title);
600
		}
601
	}
602
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
603
		left=(s_right-s_left-width+1)/2;
604
	else if(mode&WIN_RHT)
605
		left=s_right-(width+hbrdrsize+2+left);
606
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
607
		top=(api->scrn_len-height+1)/2-2;
608
	else if(mode&WIN_BOT)
609
		top=s_bottom-height-top;
610
611
	if(left<0)
		left=0;
612
613
	if(top<0)
		top=0;
614
615

	/* Dynamic Menus */
616
	if(mode&WIN_DYN
617
618
619
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
620
621
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
622
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
623
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
624
		is_redraw=1;
625
626
	}

627
628
629
630
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
631

deuce's avatar
deuce committed
632
633
634
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
635
		api->savnum=0;
deuce's avatar
deuce committed
636
637
	}

638
	if(mode&WIN_SAV) {
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
		/* Check if this screen (by cur/bar) is already saved */
		for(i=0; i<MAX_BUFS; i++) {
			if(sav[i].buf!=NULL) {
				if(cur==sav[i].cur && bar==sav[i].bar) {
					/* Yes, it is... */
					for(j=api->savnum-1; j>i; j--) {
						/* Retore old screens */
						puttext(sav[j].left,sav[j].top,sav[j].right,sav[j].bot
							,sav[j].buf);	/* put original window back */
						FREE_AND_NULL(sav[j].buf);
					}
					api->savnum=i;
				}
			}
		}
		/* savnum not the next one - must be a dynamic window or we popped back up the stack */
		if(sav[api->savnum].buf != NULL) {
			/* Is this even the right window? */
			if(sav[api->savnum].cur==cur
					&& sav[api->savnum].bar==bar) {
				if((sav[api->savnum].left!=s_left+left
					|| sav[api->savnum].top!=s_top+top
					|| sav[api->savnum].right!=s_left+left+width+1
					|| sav[api->savnum].bot!=s_top+top+height)) { /* dimensions have changed */
					puttext(sav[api->savnum].left,sav[api->savnum].top,sav[api->savnum].right,sav[api->savnum].bot
						,sav[api->savnum].buf);	/* put original window back */
					FREE_AND_NULL(sav[api->savnum].buf);
					if((sav[api->savnum].buf=(char *)malloc((width+3)*(height+2)*2))==NULL) {
						cprintf("UIFC line %d: error allocating %u bytes."
							,__LINE__,(width+3)*(height+2)*2);
						free(title);
						uifc_mouse_enable();
						return(-1);
					}
					gettext(s_left+left,s_top+top,s_left+left+width+1
						,s_top+top+height,sav[api->savnum].buf);	  /* save again */
					sav[api->savnum].left=s_left+left;
					sav[api->savnum].top=s_top+top;
					sav[api->savnum].right=s_left+left+width+1;
					sav[api->savnum].bot=s_top+top+height;
					sav[api->savnum].cur=cur;
					sav[api->savnum].bar=bar;
				}
			}
			else {
				/* Find something available... */
				while(sav[api->savnum].buf!=NULL)
					api->savnum++;
687
688
			}
		}
689
		else {
deuce's avatar
deuce committed
690
			if((sav[api->savnum].buf=(char *)malloc((width+3)*(height+2)*2))==NULL) {
691
692
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
693
				free(title);
694
				uifc_mouse_enable();
695
				return(-1);
696
			}
697
			gettext(s_left+left,s_top+top,s_left+left+width+1
698
				,s_top+top+height,sav[api->savnum].buf);
699
700
701
702
			sav[api->savnum].left=s_left+left;
			sav[api->savnum].top=s_top+top;
			sav[api->savnum].right=s_left+left+width+1;
			sav[api->savnum].bot=s_top+top+height;
703
704
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
705
		}
706
707
708
709
710
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
711
				puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
712
			if((unsigned)(s_top+height+top)<=api->scrn_len)
deuce's avatar
deuce committed
713
				puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
714
			if(left)
deuce's avatar
deuce committed
715
				puttext(1,s_top+top,s_left+left-1,s_top+height+top
716
					,blk_scrn);
717
			if(s_left+left+width<=s_right)
deuce's avatar
deuce committed
718
				puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
719
					,s_top+height+top,blk_scrn);
720
		}
deuce's avatar
deuce committed
721
		ptr=tmp_buffer;
722
723
		if(!(mode&WIN_NOBRDR)) {
			*(ptr++)='';
724
			*(ptr++)=hclr|(bclr<<4);
725

726
727
			if(api->mode&UIFC_MOUSE) {
				*(ptr++)='[';
728
				*(ptr++)=hclr|(bclr<<4);
729
730
				/* *(ptr++)=''; */
				*(ptr++)=0xfe;
731
				*(ptr++)=lclr|(bclr<<4);
732
				*(ptr++)=']';
733
				*(ptr++)=hclr|(bclr<<4);
734
735
736
				i=3;
				if(bline&BL_HELP) {
					*(ptr++)='[';
737
					*(ptr++)=hclr|(bclr<<4);
738
					*(ptr++)='?';
739
					*(ptr++)=lclr|(bclr<<4);
740
					*(ptr++)=']';
741
					*(ptr++)=hclr|(bclr<<4);
742
743
					i+=3;
				}
744
745
746
747
748
749
750
751
752
753
754
				api->buttony=s_top+top;
				api->exitstart=s_left+left+1;
				api->exitend=s_left+left+3;
				api->helpstart=s_left+left+4;
				api->helpend=s_left+left+6;
			}
			else
				i=0;

			for(;i<width-2;i++) {
				*(ptr++)='';
755
				*(ptr++)=hclr|(bclr<<4);
756
757
			}
			*(ptr++)='';
758
			*(ptr++)=hclr|(bclr<<4);
759
			*(ptr++)='';
760
			*(ptr++)=hclr|(bclr<<4);
761
762
763
764
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
765
				*(ptr++)=hclr|(bclr<<4);
766
767
768
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
769
				*(ptr++)=hclr|(bclr<<4);
770
771
772
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
773
				*(ptr++)=hclr|(bclr<<4);
774
775
			}
			*(ptr++)='';
776
			*(ptr++)=hclr|(bclr<<4);
777
			*(ptr++)='';
778
			*(ptr++)=hclr|(bclr<<4);
779
780
			for(i=0;i<width-2;i++) {
				*(ptr++)='';
781
				*(ptr++)=hclr|(bclr<<4);
782
783
			}
			*(ptr++)='';
784
			*(ptr++)=hclr|(bclr<<4);
785
786
787
788
789
790
		}

		if((*cur)>=opts)
			(*cur)=opts-1;			/* returned after scrolled */

		if(!bar) {
791
792
793
794
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
795
			i=0;
796
797
798
799
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
800
801
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
802
			if((*cur)==opts-1)
803
804
805
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
806
807
808
809
810
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
811
812
813
814
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
815
816
				(*cur)=i+(*bar);
			}
817
818
819
		}
		if((*cur)<0)
			(*cur)=0;
820

821
822
823
		j=0;
		if(i<0) i=0;
		longopt=0;
824
		while(j<height-vbrdrsize) {
825
			if(!(mode&WIN_NOBRDR)) {
826
				*(ptr++)='';
827
				*(ptr++)=hclr|(bclr<<4);
828
829
			}
			*(ptr++)=' ';
830
			*(ptr++)=hclr|(bclr<<4);
831
			*(ptr++)='';
832
			*(ptr++)=lclr|(bclr<<4);
833
			if(i==(*cur))
834
				a=lbclr;
835
			else
836
				a=lclr|(bclr<<4);
837
			if(i<opts && option[i]!=NULL) {
838
839
840
841
842
843
844
845
846
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
				for(c=0;c<b;c++) {
					*(ptr++)=option[i][c];
					*(ptr++)=a; 
				}
847
			}
848
849
			else
				c=0;
850
			while(c<width-hbrdrsize-2) {
851
				*(ptr++)=' ';
852
853
854
855
				*(ptr++)=a;
				c++;
			}
			if(!(mode&WIN_NOBRDR)) {
856
				*(ptr++)='';
857
				*(ptr++)=hclr|(bclr<<4);
858
			}
859
860
861
862
			i++;
			j++; 
		}
		if(!(mode&WIN_NOBRDR)) {
863
			*(ptr++)='';
864
			*(ptr++)=hclr|(bclr<<4);
865
866
			for(i=0;i<width-2;i++) {
				*(ptr++)='';
867
				*(ptr++)=hclr|(bclr<<4); 
868
869
			}
			*(ptr++)='';
870
			*(ptr)=hclr|(bclr<<4);	/* Not incremented to shut ot BCC */
871
872
873
874
875
876
877
878
879
880
		}
		puttext(s_left+left,s_top+top,s_left+left+width-1
			,s_top+top+height-1,tmp_buffer);
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
		if(opts+vbrdrsize>height && ((!bar && (*cur)!=opts-1)
			|| (bar && ((*cur)-(*bar))+(height-vbrdrsize)<opts))) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1);
881
			textattr(lclr|(bclr<<4));
882
			putch(31);	   /* put down arrow */
883
			textattr(hclr|(bclr<<4)); 
884
		}
885

886
887
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
888
			textattr(lclr|(bclr<<4));
889
			putch(30);	   /* put the up arrow */
890
			textattr(hclr|(bclr<<4)); 
891
		}
892

893
894
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
895
			if(bclr==BLUE) {
896
897
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
898
899
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
900
901
902
903
				puttext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
				gettext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
904
905
				for(i=1;i<width*2;i+=2)
					shade[i]=DARKGRAY;
906
907
				puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
908
			}
909
		}
910
911
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
912
913
914
915
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
916
		i=(*cur)+(top+tbrdrwidth-y);
917
918
919
		j=2;

		longopt=0;
920
		while(j<height-bbrdrwidth-1) {
deuce's avatar
deuce committed
921
			ptr=tmp_buffer;
922
			if(i==(*cur))
923
				a=lbclr;
924
			else
925
				a=lclr|(bclr<<4);
926
			if(i<opts && option[i]!=NULL) {
927
928
929
930
931
932
933
934
935
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
				for(c=0;c<b;c++) {
					*(ptr++)=option[i][c];
					*(ptr++)=a; 
				}
936
			}
937
938
			else
				c=0;
939
			while(c<width-hbrdrsize-2) {
940
941
942
943
944
945
				*(ptr++)=' ';
				*(ptr++)=a;
				c++; 
			}
			i++;
			j++; 
946
947
			puttext(s_left+left+lbrdrwidth+2,s_top+top+j,s_left+left+width-rbrdrwidth-1
				,s_top+top+j,tmp_buffer);
948
949
		}
	}
950
	free(title);
951
952
953

	last_menu_cur=cur;
	last_menu_bar=bar;
954
955
	uifc_mouse_enable();

956
	if(mode&WIN_IMM) {
957
		return(-2);
958
	}
959

960
961
962
963
	if(mode&WIN_ORG) {
		if(api->timedisplay != NULL)
			api->timedisplay(/* force? */TRUE);
	}
964

965
966
967
968
	while(1) {
	#if 0					/* debug */
		gotoxy(30,1);
		cprintf("y=%2d h=%2d c=%2d b=%2d s=%2d o=%2d"
969
			,y,height,*cur,bar ? *bar :0xff,api->savnum,opts);
970
	#endif
971
		if(api->timedisplay != NULL)
972
			api->timedisplay(/* force? */FALSE);
973
		gotkey=0;
974
		if(kbwait() || (mode&(WIN_POP|WIN_SEL))) {
deuce's avatar
deuce committed
975
			if(mode&WIN_POP)
976
977
				gotkey=ESC;
			else if(mode&WIN_SEL)
deuce's avatar
deuce committed
978
979
980
				gotkey=CR;
			else
				gotkey=inkey();
981
			if(gotkey==CIO_KEY_MOUSE) {
982
				if((gotkey=uifc_getmouse(&mevnt))==0) {
983
					/* Clicked in menu */
984
985
986
					if(mevnt.startx>=s_left+left+lbrdrwidth+2
							&& mevnt.startx<=s_left+left+width-rbrdrwidth-1
							&& mevnt.starty>=s_top+top+tbrdrwidth
987
							&& mevnt.starty<=(s_top+top+optheight)-bbrdrwidth-1
988
							&& mevnt.event==CIOLIB_BUTTON_1_CLICK) {
989

990
						(*cur)=((mevnt.starty)-(s_top+top+tbrdrwidth))+(*cur+(top+tbrdrwidth-y));
991
992
						if(bar)
							(*bar)=(*cur);
993
						y=top+tbrdrwidth+((mevnt.starty)-(s_top+top+tbrdrwidth));
994

995
						if(!opts)
996
997
							continue;

998
999
						if(mode&WIN_SAV)
							api->savnum++;
1000
						if(mode&WIN_ACT) {
1001
							uifc_mouse_disable();
1002
							if((win=(char *)alloca((width+3)*(height+2)*2))==NULL) {
1003
1004
1005
1006
1007
1008
1009
								cprintf("UIFC line %d: error allocating %u bytes."
									,__LINE__,(width+3)*(height+2)*2);
								return(-1);
							}
							gettext(s_left+left,s_top+top,s_left
								+left+width-1,s_top+top+height-1,win);
							for(i=1;i<(width*height*2);i+=2)
1010
								win[i]=lclr|(cclr<<4);
1011
							j=(((y-top)*width)*2)+7+((width-hbrdrsize-2)*2);
1012
							for(i=(((y-top)*width)*2)+7;i<j;i+=2)
1013
								win[i]=hclr|(cclr<<4);
1014
1015
1016

							puttext(s_left+left,s_top+top,s_left
								+left+width-1,s_top+top+height-1,win);
1017
							uifc_mouse_enable();
1018
1019
						}
						else if(mode&WIN_SAV) {
1020
							api->savnum--;
1021
							uifc_mouse_disable();
1022
1023
1024
							puttext(sav[api->savnum].left,sav[api->savnum].top
								,sav[api->savnum].right,sav[api->savnum].bot
								,sav[api->savnum].buf);
1025
							uifc_mouse_enable();
1026
							FREE_AND_NULL(sav[api->savnum].buf);
1027
						}
1028
1029
						if(mode&WIN_XTR && (*cur)==opts-1)
							return(MSK_INS|*cur);
1030
1031
1032
						return(*cur);
					}
					/* Clicked Scroll Up */