uifc32.c 71.2 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 2010 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
char* uifcYesNoOpts[]={"Yes","No",NULL};

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

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

112
113
void uifc_mouse_enable(void)
{
114
115
116
117
118
	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
119
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
120
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
121
122
123
124
125
126
127
128
129
	showmouse();
}

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

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

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

	c=getch();
145
	if(!c || c==0xe0)
146
		c|=(getch()<<8);
deuce's avatar
deuce committed
147
148
	return(c);
}
149

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

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

    api=uifcapi;

160
    /* install function handlers */
161
162
163
164
165
166
167
168
    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
169
	api->showbuf=showbuf;
170
	api->timedisplay=timedisplay;
171
	api->bottomline=bottomline;
172
	api->getstrxy=ugetstr;
173
	api->printf=uprintf;
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
202
203

    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;
        }
    }

204
#if 0
205
    clrscr();
206
#endif
207
208
209
210

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

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

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

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
235
236
237
238
239
240
            || 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
241
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
242
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
243
	{
244
245
246
247
248
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
249
    } else {
250
251
252
253
254
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
255
    }
256

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

    cursor=_NOCURSOR;
    _setcursortype(cursor);

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

286
287
288
289
290
291
292
	/* 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;

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

	api->initialized=TRUE;
298

299
    return(0);
300
301
}

302
303
304
305
306
307
308
309
310
311
312
313
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);
314
315
	screen=(unsigned char*)alloca(sbufsize);
	sbuffer=(unsigned char*)alloca(sbufsize);
316
317
318
	gettext(1,1,api->scrn_width,api->scrn_len+1,screen);
	while(1) {
		key=getch();
319
		if(key==0 || key==0xe0)
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
345
346
			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
347
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x10;
348
								else
deuce's avatar
deuce committed
349
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x60;
350
351
352
353
354
355
356
357
358
								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;
359
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
360
361
362
363
364
						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];
							}
365
366
367
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
							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;
		}
	}
}

384
385
386
387
388
389
390
391
392
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);
393
394
395
396
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
		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);
}

414
415
void uifcbail(void)
{
deuce's avatar
deuce committed
416
417
	int i;

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

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
436
    textattr(api->bclr|(api->cclr<<4));
437
    gotoxy(1,1);
438
    clreol();
439
440
441
442
443
    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);
444
    clreol();
445
	reset_dynamic();
446
	setname(str);
447
448
449
450
451
452
453
    return(0);
}

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

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

	now=time(NULL);
471
	if(force || difftime(now,savetime)>=60) {
472
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
473
474
475
476
477
478
479
480
481
482
483
484
		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
485
	while(c && (uchar)str[c-1]<=' ') c--;
486
487
488
489
490
491
492
493
	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
494
	, char *initial_title, char **option)
495
{
496
	uchar line[MAX_COLS*2],shade[MAX_LINES*4],*ptr
497
		,search[MAX_OPLN],bline=0,*win;
498
499
500
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
501
502
503
504
505
506
507
508
509
510
	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;
511
	int title_len;
512
	struct mouse_event mevnt;
513
	char	*title=NULL;
514
	int	a,b,c,longopt;
515
	int	optheight=0;
516
	int gotkey;
517
518
519
520
521
522
523
524
525
526
527
528
529
530
	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;
	}
531
532
	title=strdup(initial_title==NULL?"":initial_title);

533
	uifc_mouse_disable();
534
535

	title_len=strlen(title);
536
537
538

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

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

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

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

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

640
	if(mode&WIN_SAV) {
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
687
688
		/* 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++;
689
690
			}
		}
691
		else {
deuce's avatar
deuce committed
692
			if((sav[api->savnum].buf=(char *)malloc((width+3)*(height+2)*2))==NULL) {
693
694
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
695
				free(title);
696
				uifc_mouse_enable();
697
				return(-1);
698
			}
699
			gettext(s_left+left,s_top+top,s_left+left+width+1
700
				,s_top+top+height,sav[api->savnum].buf);
701
702
703
704
			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;
705
706
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
707
		}
708
709
710
711
712
	}

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

728
729
			if(api->mode&UIFC_MOUSE) {
				*(ptr++)='[';
730
				*(ptr++)=hclr|(bclr<<4);
731
732
				/* *(ptr++)=''; */
				*(ptr++)=0xfe;
733
				*(ptr++)=lclr|(bclr<<4);
734
				*(ptr++)=']';
735
				*(ptr++)=hclr|(bclr<<4);
736
737
738
				i=3;
				if(bline&BL_HELP) {
					*(ptr++)='[';
739
					*(ptr++)=hclr|(bclr<<4);
740
					*(ptr++)='?';
741
					*(ptr++)=lclr|(bclr<<4);
742
					*(ptr++)=']';
743
					*(ptr++)=hclr|(bclr<<4);
744
745
					i+=3;
				}
746
747
748
749
750
751
752
753
754
755
756
				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++)='';
757
				*(ptr++)=hclr|(bclr<<4);
758
759
			}
			*(ptr++)='';
760
			*(ptr++)=hclr|(bclr<<4);
761
			*(ptr++)='';
762
			*(ptr++)=hclr|(bclr<<4);
763
764
765
766
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
767
				*(ptr++)=hclr|(bclr<<4);
768
769
770
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
771
				*(ptr++)=hclr|(bclr<<4);
772
773
774
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
775
				*(ptr++)=hclr|(bclr<<4);
776
777
			}
			*(ptr++)='';
778
			*(ptr++)=hclr|(bclr<<4);
779
			*(ptr++)='';
780
			*(ptr++)=hclr|(bclr<<4);
781
782
			for(i=0;i<width-2;i++) {
				*(ptr++)='';
783
				*(ptr++)=hclr|(bclr<<4);
784
785
			}
			*(ptr++)='';
786
			*(ptr++)=hclr|(bclr<<4);
787
788
789
790
791
792
		}

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

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

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

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

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

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

	last_menu_cur=cur;
	last_menu_bar=bar;
956
957
	uifc_mouse_enable();

958
	if(mode&WIN_IMM) {
959
		return(-2);
960
	}
961

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

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

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

997
						if(!opts)
998
999
							continue;

1000
1001
						if(mode&WIN_SAV)
							api->savnum++;
1002
						if(mode&WIN_ACT) {
1003
							uifc_mouse_disable();
1004
							if((win=(char *)alloca((width+3)*(height+2)*2))==NULL) {
1005
1006
1007
1008
1009
1010
1011
								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)
1012
								win[i]=lclr|(cclr<<4);
1013
							j=(((y-top)*width)*2)+7+((width-hbrdrsize-2)*2);
1014
							for(i=(((y-top)*width)*2)+7;i<j;i+=2)
1015
								win[i]=hclr|(cclr<<4);
1016
1017
1018

							puttext(s_left+left,s_top+top,s_left
								+left+width-1,s_top+top+height-1,win);
1019
							uifc_mouse_enable();
1020
1021
						}
						else if(mode&WIN_SAV) {
1022
							api->savnum--;
1023
							uifc_mouse_disable();
1024
1025
1026
							puttext(sav[api->savnum].left,sav[api->savnum].top
								,sav[api->savnum].right,sav[api->savnum].bot
								,sav[api->savnum].buf);