uifc32.c 75.1 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
48
49
 *																			*
 * 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>
	#define mswait(x) Sleep(x)
#endif
50
#include <genwrap.h>	// for alloca()
51

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

#define BLINK	128

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

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

/* 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;
94
static int save_menu_opts=-1;
95

96
97
char* uifcYesNoOpts[]={"Yes","No",NULL};

98
99
100
101
102
static void reset_dynamic(void) {
	last_menu_cur=NULL;
	last_menu_bar=NULL;
	save_menu_cur=-1;
	save_menu_bar=-1;
103
	save_menu_opts=-1;
104
105
}

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
static uifc_graphics_t default_chars = {
	.background=0xb0,
	.help_char='?',
	.close_char=0xfe,
	.up_arrow=30,
	.down_arrow=31,
	.button_left='[',
	.button_right=']',

	.list_top_left=0xc9,
	.list_top=0xcd,
	.list_top_right=0xbb,
	.list_separator_left=0xcc,
	.list_separator_right=0xb9,
	.list_horizontal_separator=0xcd,
	.list_left=0xba,
	.list_right=0xba,
	.list_bottom_left=0xc8,
	.list_bottom_right=0xbc,
	.list_bottom=0xcd,
	.list_scrollbar_separator=0xb3,

	.input_top_left=0xc9,
	.input_top=0xcd,
	.input_top_right=0xbb,
	.input_left=0xba,
	.input_right=0xba,
	.input_bottom_left=0xc8,
	.input_bottom_right=0xbc,
	.input_bottom=0xcd,

	.popup_top_left=0xda,
	.popup_top=0xc4,
	.popup_top_right=0xbf,
	.popup_left=0xb3,
	.popup_right=0xb3,
	.popup_bottom_left=0xc0,
	.popup_bottom_right=0xd9,
	.popup_bottom=0xc4,

	.help_top_left=0xda,
	.help_top=0xc4,
	.help_top_right=0xbf,
	.help_left=0xb3,
	.help_right=0xb3,
	.help_bottom_left=0xc0,
	.help_bottom_right=0xd9,
	.help_bottom=0xc4,
	.help_titlebreak_left=0xb4,
	.help_titlebreak_right=0xc3,
	.help_hitanykey_left=0xb4,
	.help_hitanykey_right=0xc3,
};

160
161
162
163
164
/****************************************************************************/
/* Initialization function, see uifc.h for details.							*/
/* Returns 0 on success.													*/
/****************************************************************************/

165
166
void uifc_mouse_enable(void)
{
167
168
169
170
171
	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
172
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
173
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
174
175
176
177
178
179
180
181
182
	showmouse();
}

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

deuce's avatar
deuce committed
183
int kbwait(void) {
184
	int timeout=0;
185
	while(timeout++<50) {
186
		if(kbhit())
187
			return(TRUE);
188
		mswait(1);
189
190
191
192
	}
	return(FALSE);
}

193
int inkey(void)
deuce's avatar
deuce committed
194
195
196
197
{
	int c;

	c=getch();
198
	if(!c || c==0xe0)
199
		c|=(getch()<<8);
deuce's avatar
deuce committed
200
201
	return(c);
}
202

deuce's avatar
deuce committed
203
int UIFCCALL uifcini32(uifcapi_t* uifcapi)
204
{
205
	unsigned	i;
206
207
208
209
210
211
	struct	text_info txtinfo;

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

    api=uifcapi;
212
213
    if (api->chars == NULL)
		api->chars = &default_chars;
214

215
    /* install function handlers */
216
217
218
219
220
221
222
223
    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
224
	api->showbuf=showbuf;
225
	api->timedisplay=timedisplay;
226
	api->bottomline=bottomline;
227
	api->getstrxy=ugetstr;
228
	api->printf=uprintf;
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

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

259
#if 0
260
    clrscr();
261
#endif
262
263
264
265

    gettextinfo(&txtinfo);
    /* unsupported mode? */
    if(txtinfo.screenheight<MIN_LINES
deuce's avatar
deuce committed
266
/*        || txtinfo.screenheight>MAX_LINES */
deuce's avatar
deuce committed
267
        || txtinfo.screenwidth<40) {
268
269
270
        textmode(C80);  /* set mode to 80x25*/
        gettextinfo(&txtinfo);
    }
271
	window(1,1,txtinfo.screenwidth,txtinfo.screenheight);
272
273

    api->scrn_len=txtinfo.screenheight;
deuce's avatar
deuce committed
274
275
276
    if(api->scrn_len<MIN_LINES) {
        cprintf("\7UIFC: Screen length (%u) must be %d lines or greater\r\n"
            ,api->scrn_len,MIN_LINES);
277
278
279
280
        return(-2);
    }
    api->scrn_len--; /* account for status line */

281
282
    if(txtinfo.screenwidth<40) {
        cprintf("\7UIFC: Screen width (%u) must be at least 40 characters\r\n"
283
284
285
286
287
288
289
            ,txtinfo.screenwidth);
        return(-3);
    }
	api->scrn_width=txtinfo.screenwidth;

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
290
291
292
293
294
295
            || 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
296
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
297
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
298
	{
299
300
301
302
303
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
304
    } else {
305
306
307
308
309
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
310
    }
311

deuce's avatar
deuce committed
312
	blk_scrn_len=api->scrn_width*api->scrn_len*2;
deuce's avatar
deuce committed
313
	if((blk_scrn=(char *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
314
315
316
317
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
318
	if((tmp_buffer=(uchar *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
319
320
321
322
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
323
	if((tmp_buffer2=(uchar *)malloc(blk_scrn_len))==NULL)  {
324
325
326
327
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
328
    for(i=0;i<blk_scrn_len;i+=2) {
329
        blk_scrn[i]=api->chars->background;
330
        blk_scrn[i+1]=api->cclr|(api->bclr<<4);
331
332
333
334
335
    }

    cursor=_NOCURSOR;
    _setcursortype(cursor);

336
	if(cio_api.mouse) {
337
		api->mode|=UIFC_MOUSE;
338
339
		uifc_mouse_enable();
	}
340

341
342
343
344
345
346
347
	/* 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;

348
349
	for(i=0; i<MAX_BUFS; i++)
		sav[i].buf=NULL;
deuce's avatar
deuce committed
350
351
352
	api->savnum=0;

	api->initialized=TRUE;
353

354
    return(0);
355
356
}

357
358
359
360
361
362
363
364
365
366
367
368
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);
369
370
	screen=(unsigned char*)alloca(sbufsize);
	sbuffer=(unsigned char*)alloca(sbufsize);
371
372
373
	gettext(1,1,api->scrn_width,api->scrn_len+1,screen);
	while(1) {
		key=getch();
374
		if(key==0 || key==0xe0)
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
			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
402
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x10;
403
								else
deuce's avatar
deuce committed
404
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x60;
405
406
407
408
409
410
411
412
413
								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;
414
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
415
416
417
418
419
						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];
							}
420
421
422
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
							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;
		}
	}
}

439
440
441
442
443
444
445
446
447
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);
448
449
450
451
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
		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);
}

469
470
void uifcbail(void)
{
deuce's avatar
deuce committed
471
472
	int i;

473
474
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
475
	uifc_mouse_disable();
476
	suspendciolib();
deuce's avatar
deuce committed
477
478
479
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
480
	api->initialized=FALSE;
deuce's avatar
deuce committed
481
482
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
483
484
485
486
487
488
489
490
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
491
    textattr(api->bclr|(api->cclr<<4));
492
    gotoxy(1,1);
493
    clreol();
494
495
496
497
498
    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);
499
    clreol();
500
	reset_dynamic();
501
	setname(str);
502
503
504
505
506
507
508
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
509
	gettext(x1,y1,x2,y2,tmp_buffer2);
510
	if(down)
511
		puttext(x1,y1+1,x2,y2,tmp_buffer2);
512
	else
513
		puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)*2));
514
515
516
517
518
519
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
520
static void timedisplay(BOOL force)
521
522
523
524
525
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
526
	if(force || difftime(now,savetime)>=60) {
527
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
528
529
530
531
532
533
534
		savetime=now; 
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
535
static void truncspctrl(char *str)
536
537
538
539
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
540
	while(c && (uchar)str[c-1]<=' ') c--;
541
542
543
544
545
546
547
548
	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
549
	, char *initial_title, char **option)
550
{
551
	uchar line[MAX_COLS*2],shade[MAX_LINES*4],*ptr
552
553
		,bline=0,*win;
    char search[MAX_OPLN];
554
555
556
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
557
558
559
560
561
562
563
564
565
566
	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;
567
	int title_len;
568
	struct mouse_event mevnt;
569
	char	*title=NULL;
570
	int	a,b,c,longopt;
571
	int	optheight=0;
572
	int gotkey;
573
574
575
576
577
578
579
580
581
582
583
584
585
586
	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;
	}
587
588
	title=strdup(initial_title==NULL?"":initial_title);

589
590
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
591
592

	title_len=strlen(title);
593
594
595

	if(mode&WIN_FAT) {
		s_top=1;
596
		s_left=2;
597
598
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
599
	}
600
601
602
603
604
605
606
607
608
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}

609
610
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
611
	if(api->helpbuf!=NULL || api->helpixbfile[0]!=0) bline|=BL_HELP;
612
613
614
615
	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;
616
	if(mode&WIN_EDIT) bline|=BL_EDIT;
617
618
	if(api->bottomline != NULL)
		api->bottomline(bline);
619
	while(option!=NULL && opts<MAX_OPTS)
620
		if(option[opts]==NULL || option[opts][0]==0)
621
622
			break;
		else opts++;
deuce's avatar
deuce committed
623
	if(mode&WIN_XTR && opts<MAX_OPTS)
624
		opts++;
625
	optheight=opts+vbrdrsize;
626
	height=optheight;
627
628
629
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
630
631
	if(top+height>s_bottom)
		height=(s_bottom)-top;
632
633
	if(optheight>height)
		optheight=height;
634
635
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
636
		for(i=0;i<opts;i++) {
637
			if(option[i]!=NULL) {
638
				truncspctrl(option[i]);
639
640
641
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
642
643
		}
	}
644
645
646
647
648
649
650
	/* 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;
	}
651
652
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
653
654
655
656
657
		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;
658
			title_len=strlen(title);
659
		}
660
	}
661
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
662
		left=(s_right-s_left-width+1)/2;
663
	else if(mode&WIN_RHT)
664
		left=s_right-(width+hbrdrsize+2+left);
665
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
666
		top=(api->scrn_len-height+1)/2-2;
667
	else if(mode&WIN_BOT)
668
		top=s_bottom-height-top;
669
670
	if(left<0)
		left=0;
671
672
	if(top<0)
		top=0;
673
674

	/* Dynamic Menus */
675
	if(mode&WIN_DYN
676
677
678
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
679
680
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
681
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
682
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
683
		is_redraw=1;
684
685
	}

686
687
688
689
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
690

deuce's avatar
deuce committed
691
692
693
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
694
		api->savnum=0;
deuce's avatar
deuce committed
695
696
	}

697
	if(mode&WIN_SAV) {
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
		/* 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);
725
					if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
726
727
728
						cprintf("UIFC line %d: error allocating %u bytes."
							,__LINE__,(width+3)*(height+2)*2);
						free(title);
729
730
						if(!(api->mode&UIFC_NHM))
							uifc_mouse_enable();
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
						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++;
747
748
			}
		}
749
		else {
750
			if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
751
752
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
753
				free(title);
754
755
				if(!(api->mode&UIFC_NHM))
					uifc_mouse_enable();
756
				return(-1);
757
			}
758
			gettext(s_left+left,s_top+top,s_left+left+width+1
759
				,s_top+top+height,sav[api->savnum].buf);
760
761
762
763
			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;
764
765
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
766
		}
767
768
769
770
771
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
772
				puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
773
			if((unsigned)(s_top+height+top)<=api->scrn_len)
deuce's avatar
deuce committed
774
				puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
775
			if(left)
deuce's avatar
deuce committed
776
				puttext(1,s_top+top,s_left+left-1,s_top+height+top
777
					,blk_scrn);
778
			if(s_left+left+width<=s_right)
deuce's avatar
deuce committed
779
				puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
780
					,s_top+height+top,blk_scrn);
781
		}
deuce's avatar
deuce committed
782
		ptr=tmp_buffer;
783
		if(!(mode&WIN_NOBRDR)) {
784
			*(ptr++)=api->chars->list_top_left;
785
			*(ptr++)=hclr|(bclr<<4);
786

787
			if(api->mode&UIFC_MOUSE) {
788
				*(ptr++)=api->chars->button_left;
789
				*(ptr++)=hclr|(bclr<<4);
790
				/* *(ptr++)=''; */
791
				*(ptr++)=api->chars->close_char;
792
				*(ptr++)=lclr|(bclr<<4);
793
				*(ptr++)=api->chars->button_right;
794
				*(ptr++)=hclr|(bclr<<4);
795
796
				i=3;
				if(bline&BL_HELP) {
797
					*(ptr++)=api->chars->button_left;
798
					*(ptr++)=hclr|(bclr<<4);
799
					*(ptr++)=api->chars->help_char;
800
					*(ptr++)=lclr|(bclr<<4);
801
					*(ptr++)=api->chars->button_right;
802
					*(ptr++)=hclr|(bclr<<4);
803
804
					i+=3;
				}
805
806
807
808
809
810
811
812
813
814
				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++) {
815
				*(ptr++)=api->chars->list_top;
816
				*(ptr++)=hclr|(bclr<<4);
817
			}
818
			*(ptr++)=api->chars->list_top_right;
819
			*(ptr++)=hclr|(bclr<<4);
820
			*(ptr++)=api->chars->list_left;
821
			*(ptr++)=hclr|(bclr<<4);
822
823
824
825
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
826
				*(ptr++)=hclr|(bclr<<4);
827
828
829
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
830
				*(ptr++)=hclr|(bclr<<4);
831
832
833
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
834
				*(ptr++)=hclr|(bclr<<4);
835
			}
836
			*(ptr++)=api->chars->list_right;
837
			*(ptr++)=hclr|(bclr<<4);
838
			*(ptr++)=api->chars->list_separator_left;
839
			*(ptr++)=hclr|(bclr<<4);
840
			for(i=0;i<width-2;i++) {
841
				*(ptr++)=api->chars->list_horizontal_separator;
842
				*(ptr++)=hclr|(bclr<<4);
843
			}
844
			*(ptr++)=api->chars->list_separator_right;
845
			*(ptr++)=hclr|(bclr<<4);
846
847
848
849
850
851
		}

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

		if(!bar) {
852
853
854
855
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
856
			i=0;
857
858
859
860
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
861
862
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
863
			if((*cur)==opts-1)
864
865
866
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
867
868
869
870
871
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
872
873
874
875
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
876
877
				(*cur)=i+(*bar);
			}
878
879
880
		}
		if((*cur)<0)
			(*cur)=0;
881

882
883
884
		j=0;
		if(i<0) i=0;
		longopt=0;
885
		while(j<height-vbrdrsize) {
886
			if(!(mode&WIN_NOBRDR)) {
887
				*(ptr++)=api->chars->list_left;
888
				*(ptr++)=hclr|(bclr<<4);
889
890
			}
			*(ptr++)=' ';
891
			*(ptr++)=hclr|(bclr<<4);
892
			*(ptr++)=api->chars->list_scrollbar_separator;
893
			*(ptr++)=lclr|(bclr<<4);
894
			if(i==(*cur))
895
				a=lbclr;
896
			else
897
				a=lclr|(bclr<<4);
898
			if(i<opts && option[i]!=NULL) {
899
900
901
902
903
904
905
906
907
				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; 
				}
908
			}
909
910
			else
				c=0;
911
			while(c<width-hbrdrsize-2) {
912
				*(ptr++)=' ';
913
914
915
916
				*(ptr++)=a;
				c++;
			}
			if(!(mode&WIN_NOBRDR)) {
917
				*(ptr++)=api->chars->list_right;
918
				*(ptr++)=hclr|(bclr<<4);
919
			}
920
921
922
923
			i++;
			j++; 
		}
		if(!(mode&WIN_NOBRDR)) {
924
			*(ptr++)=api->chars->list_bottom_left;
925
			*(ptr++)=hclr|(bclr<<4);
926
			for(i=0;i<width-2;i++) {
927
				*(ptr++)=api->chars->list_bottom;
928
				*(ptr++)=hclr|(bclr<<4); 
929
			}
930
			*(ptr++)=api->chars->list_bottom_right;
931
			*(ptr)=hclr|(bclr<<4);	/* Not incremented to shut ot BCC */
932
933
934
935
936
937
938
939
940
941
		}
		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);
942
			textattr(lclr|(bclr<<4));
943
			putch(api->chars->down_arrow);	   /* put down arrow */
944
			textattr(hclr|(bclr<<4)); 
945
		}
946

947
948
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
949
			textattr(lclr|(bclr<<4));
950
			putch(api->chars->up_arrow);	   /* put the up arrow */
951
			textattr(hclr|(bclr<<4)); 
952
		}
953

954
955
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
956
			if(bclr==BLUE) {
957
958
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
959
960
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
961
962
963
964
				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);
965
966
				for(i=1;i<width*2;i+=2)
					shade[i]=DARKGRAY;
967
968
				puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
969
			}
970
		}
971
972
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
973
974
975
976
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
977
		i=(*cur)+(top+tbrdrwidth-y);
978
979
980
		j=2;

		longopt=0;
981
		while(j<height-bbrdrwidth-1) {
deuce's avatar
deuce committed
982
			ptr=tmp_buffer;
983
			if(i==(*cur))
984
				a=lbclr;
985
			else
986
				a=lclr|(bclr<<4);
987
			if(i<opts && option[i]!=NULL) {
988
989
990
991
992
993
994
995
996
				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; 
				}
997
			}
998
999
			else
				c=0;
1000
			while(c<width-hbrdrsize-2) {
1001
1002
1003
1004
1005
1006
				*(ptr++)=' ';
				*(ptr++)=a;
				c++; 
			}
			i++;
			j++; 
1007
1008
			puttext(s_left+left+lbrdrwidth+2,s_top+top+j,s_left+left+width-rbrdrwidth-1
				,s_top+top+j,tmp_buffer);
1009
1010
		}
	}
1011
	free(title);
1012
1013
1014

	last_menu_cur=cur;
	last_menu_bar=bar;
1015
1016
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_enable();
1017

1018
	if(mode&WIN_IMM) {
1019
		return(-2);
1020
	}
1021

1022
1023
1024
1025
	if(mode&WIN_ORG) {
		if(api->timedisplay != NULL)
			api->timedisplay(/* force? */TRUE);
	}
1026

1027
1028
1029
1030
	while(1) {
	#if 0					/* debug */
		gotoxy(30,1);
		cprintf("y=%2d h=%2d c=%2d b=%2d s=%2d o=%2d"
1031
			,y,height,*cur,bar ? *bar :0xff,api->savnum,opts);
1032
	#endif
1033
		if(api->timedisplay != NULL)
1034
			api->timedisplay(/* force? */FALSE);
1035
		gotkey=0;
1036
1037
		textattr(((api->lbclr)&0x0f)|((api->lbclr >> 4)&0x0f));
		gotoxy(s_left+lbrdrwidth+2+left, s_top+y);
1038
		if(kbwait() || (mode&(WIN_POP|WIN_SEL))) {
deuce's avatar
deuce committed
1039
			if(mode&WIN_POP)
1040
1041
				gotkey=ESC;
			else if(mode&WIN_SEL)
deuce's avatar
deuce committed
1042
1043
1044
				gotkey=CR;
			else
				gotkey=inkey();