uifc32.c 78.9 KB
Newer Older
1
2
/* Curses implementation of UIFC (user interface) library based on uifc.c */

rswindell's avatar
rswindell committed
3
4
/* $Id$ */

5
6
7
8
/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10
11
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>
	#define mswait(x) Sleep(x)
#endif
48
#include <genwrap.h>	// for alloca()
49

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

#define BLINK	128

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

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

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

94
95
char* uifcYesNoOpts[]={"Yes","No",NULL};

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

104
static uifc_graphics_t cp437_chars = {
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
	.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,
};

158
159
160
161
162
static uifc_graphics_t ascii_chars = {
	.background='#',
	.help_char='?',
	.close_char='X',
	.up_arrow='^',
163
	.down_arrow='v',
164
165
166
	.button_left='[',
	.button_right=']',

167
	.list_top_left=',',
168
	.list_top='-',
169
	.list_top_right='.',
170
171
172
173
174
	.list_separator_left='+',
	.list_separator_right='+',
	.list_horizontal_separator='-',
	.list_left='|',
	.list_right='|',
175
176
	.list_bottom_left='`',
	.list_bottom_right='\'',
177
178
179
	.list_bottom='-',
	.list_scrollbar_separator='|',

180
	.input_top_left=',',
181
	.input_top='-',
182
	.input_top_right='.',
183
184
	.input_left='|',
	.input_right='|',
185
186
	.input_bottom_left='`',
	.input_bottom_right='\'',
187
188
	.input_bottom='-',

189
	.popup_top_left=',',
190
	.popup_top='-',
191
	.popup_top_right='.',
192
193
	.popup_left='|',
	.popup_right='|',
194
195
	.popup_bottom_left='`',
	.popup_bottom_right='\'',
196
197
	.popup_bottom='-',

198
	.help_top_left=',',
199
	.help_top='-',
200
	.help_top_right='.',
201
202
	.help_left='|',
	.help_right='|',
203
204
	.help_bottom_left='`',
	.help_bottom_right='\'',
205
206
207
208
209
210
211
	.help_bottom='-',
	.help_titlebreak_left='|',
	.help_titlebreak_right='|',
	.help_hitanykey_left='|',
	.help_hitanykey_right='|',
};

212
213
214
215
216
/****************************************************************************/
/* Initialization function, see uifc.h for details.							*/
/* Returns 0 on success.													*/
/****************************************************************************/

217
218
void uifc_mouse_enable(void)
{
219
220
221
222
223
	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
224
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
225
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
226
227
228
229
230
231
232
233
234
	showmouse();
}

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

deuce's avatar
deuce committed
235
int kbwait(void) {
236
	int timeout=0;
237
	while(timeout++<50) {
238
		if(kbhit())
239
			return(TRUE);
240
		mswait(1);
241
242
243
244
	}
	return(FALSE);
}

245
int inkey(void)
deuce's avatar
deuce committed
246
247
248
249
{
	int c;

	c=getch();
250
	if(!c || c==0xe0)
251
		c|=(getch()<<8);
deuce's avatar
deuce committed
252
253
	return(c);
}
254

deuce's avatar
deuce committed
255
int UIFCCALL uifcini32(uifcapi_t* uifcapi)
256
{
257
	unsigned	i;
258
259
260
261
262
263
	struct	text_info txtinfo;

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

    api=uifcapi;
264
265
    if (api->chars == NULL) {
		switch(getfont()) {
266
			case -1:
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
			case 0:
			case 17:
			case 18:
			case 19:
			case 25:
			case 26:
			case 27:
			case 28:
			case 29:
			case 31:
				api->chars = &cp437_chars;
				break;
			default:
				api->chars = &ascii_chars;
				break;
		}
	}
284

285
    /* install function handlers */
286
287
288
289
290
291
292
293
    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
294
	api->showbuf=showbuf;
295
	api->timedisplay=timedisplay;
296
	api->bottomline=bottomline;
297
	api->getstrxy=ugetstr;
298
	api->printf=uprintf;
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328

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

329
#if 0
330
    clrscr();
331
#endif
332
333
334
335

    gettextinfo(&txtinfo);
    /* unsupported mode? */
    if(txtinfo.screenheight<MIN_LINES
deuce's avatar
deuce committed
336
/*        || txtinfo.screenheight>MAX_LINES */
deuce's avatar
deuce committed
337
        || txtinfo.screenwidth<40) {
338
339
340
        textmode(C80);  /* set mode to 80x25*/
        gettextinfo(&txtinfo);
    }
341
	window(1,1,txtinfo.screenwidth,txtinfo.screenheight);
342
343

    api->scrn_len=txtinfo.screenheight;
deuce's avatar
deuce committed
344
345
346
    if(api->scrn_len<MIN_LINES) {
        cprintf("\7UIFC: Screen length (%u) must be %d lines or greater\r\n"
            ,api->scrn_len,MIN_LINES);
347
348
349
350
        return(-2);
    }
    api->scrn_len--; /* account for status line */

351
352
    if(txtinfo.screenwidth<40) {
        cprintf("\7UIFC: Screen width (%u) must be at least 40 characters\r\n"
353
354
355
356
357
358
359
            ,txtinfo.screenwidth);
        return(-3);
    }
	api->scrn_width=txtinfo.screenwidth;

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
360
361
362
363
364
365
            || 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
366
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
367
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
368
	{
369
370
371
372
373
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
374
    } else {
375
376
377
378
379
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
380
    }
381

deuce's avatar
deuce committed
382
	blk_scrn_len=api->scrn_width*api->scrn_len*2;
deuce's avatar
deuce committed
383
	if((blk_scrn=(char *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
384
385
386
387
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
388
	if((tmp_buffer=(uchar *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
389
390
391
392
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
393
	if((tmp_buffer2=(uchar *)malloc(blk_scrn_len))==NULL)  {
394
395
396
397
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
398
    for(i=0;i<blk_scrn_len;i+=2) {
399
        blk_scrn[i]=api->chars->background;
400
        blk_scrn[i+1]=api->cclr|(api->bclr<<4);
401
402
403
404
405
    }

    cursor=_NOCURSOR;
    _setcursortype(cursor);

406
	if(cio_api.mouse && !(api->mode&UIFC_NOMOUSE)) {
407
		api->mode|=UIFC_MOUSE;
408
409
		uifc_mouse_enable();
	}
410

411
412
413
414
415
416
417
	/* 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;

418
419
	for(i=0; i<MAX_BUFS; i++)
		sav[i].buf=NULL;
deuce's avatar
deuce committed
420
421
422
	api->savnum=0;

	api->initialized=TRUE;
423

424
    return(0);
425
426
}

427
428
429
430
431
432
433
434
435
436
437
438
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);
439
440
	screen=(unsigned char*)alloca(sbufsize);
	sbuffer=(unsigned char*)alloca(sbufsize);
441
442
443
	gettext(1,1,api->scrn_width,api->scrn_len+1,screen);
	while(1) {
		key=getch();
444
		if(key==0 || key==0xe0)
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
			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
472
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x10;
473
								else
deuce's avatar
deuce committed
474
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x60;
475
476
477
478
479
480
481
482
483
								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;
484
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
485
486
487
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
488
								copybuf[outpos++]=screen[(y*api->scrn_width+x)*2]?screen[(y*api->scrn_width+x)*2]:' ';
489
							}
490
491
492
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
							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;
		}
	}
}

509
510
511
512
513
514
515
516
517
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);
518
519
520
521
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
		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);
}

539
540
void uifcbail(void)
{
deuce's avatar
deuce committed
541
542
	int i;

543
544
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
545
	uifc_mouse_disable();
546
	suspendciolib();
deuce's avatar
deuce committed
547
548
549
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
550
	api->initialized=FALSE;
deuce's avatar
deuce committed
551
552
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
553
554
555
556
557
558
559
560
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
561
    textattr(api->bclr|(api->cclr<<4));
562
    gotoxy(1,1);
563
    clreol();
564
565
566
567
568
    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);
569
    clreol();
570
	reset_dynamic();
571
	setname(str);
572
573
574
575
576
577
578
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
579
	gettext(x1,y1,x2,y2,tmp_buffer2);
580
	if(down)
581
		puttext(x1,y1+1,x2,y2,tmp_buffer2);
582
	else
583
		puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)*2));
584
585
586
587
588
589
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
590
static void timedisplay(BOOL force)
591
592
593
594
595
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
596
	if(force || difftime(now,savetime)>=60) {
597
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
598
599
600
601
602
603
604
		savetime=now; 
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
605
static void truncspctrl(char *str)
606
607
608
609
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
610
	while(c && (uchar)str[c-1]<=' ') c--;
611
612
613
614
615
616
617
618
	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
619
	, char *initial_title, char **option)
620
{
621
	uchar line[MAX_COLS*2],shade[MAX_LINES*4],*ptr
622
623
		,bline=0,*win;
    char search[MAX_OPLN];
624
625
626
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
627
628
629
630
631
632
633
634
635
636
	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;
637
	int title_len;
638
	int tmpcur=0;
639
	struct mouse_event mevnt;
640
	char	*title=NULL;
641
	int	a,b,c,longopt;
642
	int	optheight=0;
643
	int gotkey;
644
645
	uchar	hclr,lclr,bclr,cclr,lbclr;

646
	if(cur==NULL) cur=&tmpcur;
647
	api->exit_flags = 0;
648
649
650
651
652
653
654
655
656
657
658
659
	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;
	}
660
661
	title=strdup(initial_title==NULL?"":initial_title);

662
663
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
664
665

	title_len=strlen(title);
666
667
668

	if(mode&WIN_FAT) {
		s_top=1;
669
		s_left=2;
670
671
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
672
	}
673
674
675
676
677
678
679
680
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}
681
682
683
684
685
686
687
688
	/* Count the options */
	while (option != NULL && opts < MAX_OPTS) {
		if (option[opts] == NULL || option[opts][0] == 0)
			break;
		else opts++;
	}
	if (mode&WIN_XTR && opts<MAX_OPTS)
		opts++;
689

690
	/* Sanity-check the savnum */
691
692
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
693
694

	/* Create the status bar/bottom-line */
695
	if(api->helpbuf!=NULL || api->helpixbfile[0]!=0) bline|=BL_HELP;
696
697
	if(mode&WIN_INS) bline|=BL_INS;
	if(mode&WIN_DEL) bline|=BL_DEL;
698
699
	if(mode&WIN_COPY) bline|=BL_COPY;
	if(mode&WIN_PASTE) bline|=BL_PASTE;
700
	if(mode&WIN_EDIT) bline|=BL_EDIT;
701
702
	if (api->bottomline != NULL) {
		if ((mode&(WIN_XTR | WIN_PASTEXTR)) == WIN_XTR && (*cur) == opts - 1)
703
			api->bottomline(bline & ~BL_PASTE);
704
705
706
		else
			api->bottomline(bline);
	}
707
	optheight=opts+vbrdrsize;
708
	height=optheight;
709
710
711
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
712
713
	if(top+height>s_bottom)
		height=(s_bottom)-top;
714
715
	if(optheight>height)
		optheight=height;
716
717
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
718
		for(i=0;i<opts;i++) {
719
			if(option[i]!=NULL) {
720
				truncspctrl(option[i]);
721
722
723
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
724
725
		}
	}
726
	/* Determine minimum widths here to accommodate mouse "icons" in border */
727
728
729
730
731
732
	if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) {
		if(bline&BL_HELP && width<8)
			width=8;
		else if(width<5)
			width=5;
	}
733
734
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
735
736
737
738
739
		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;
740
			title_len=strlen(title);
741
		}
742
	}
743
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
744
		left=(s_right-s_left-width+1)/2;
745
	else if(mode&WIN_RHT)
746
		left=s_right-(width+hbrdrsize+2+left);
747
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
748
		top=(api->scrn_len-height+1)/2-2;
749
	else if(mode&WIN_BOT)
750
		top=s_bottom-height-top;
751
752
	if(left<0)
		left=0;
753
754
	if(top<0)
		top=0;
755
756

	/* Dynamic Menus */
757
	if(mode&WIN_DYN
758
759
760
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
761
762
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
763
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
764
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
765
		is_redraw=1;
766
767
	}

768
769
770
771
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
772

deuce's avatar
deuce committed
773
774
775
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
776
		api->savnum=0;
deuce's avatar
deuce committed
777
778
	}

779
	if(mode&WIN_SAV) {
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
		/* 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);
807
					if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
808
809
810
						cprintf("UIFC line %d: error allocating %u bytes."
							,__LINE__,(width+3)*(height+2)*2);
						free(title);
811
812
						if(!(api->mode&UIFC_NHM))
							uifc_mouse_enable();
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
						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++;
829
830
			}
		}
831
		else {
832
			if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
833
834
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
835
				free(title);
836
837
				if(!(api->mode&UIFC_NHM))
					uifc_mouse_enable();
838
				return(-1);
839
			}
840
			gettext(s_left+left,s_top+top,s_left+left+width+1
841
				,s_top+top+height,sav[api->savnum].buf);
842
843
844
845
			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;
846
847
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
848
		}
849
850
851
852
853
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
854
				puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
855
			if((unsigned)(s_top+height+top)<=api->scrn_len)
deuce's avatar
deuce committed
856
				puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
857
			if(left)
deuce's avatar
deuce committed
858
				puttext(1,s_top+top,s_left+left-1,s_top+height+top
859
					,blk_scrn);
860
			if(s_left+left+width<=s_right)
deuce's avatar
deuce committed
861
				puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
862
					,s_top+height+top,blk_scrn);
863
		}
deuce's avatar
deuce committed
864
		ptr=tmp_buffer;
865
		if(!(mode&WIN_NOBRDR)) {
866
			*(ptr++)=api->chars->list_top_left;
867
			*(ptr++)=hclr|(bclr<<4);
868

869
			if(api->mode&UIFC_MOUSE) {
870
				*(ptr++)=api->chars->button_left;
871
				*(ptr++)=hclr|(bclr<<4);
872
				/* *(ptr++)=''; */
873
				*(ptr++)=api->chars->close_char;
874
				*(ptr++)=lclr|(bclr<<4);
875
				*(ptr++)=api->chars->button_right;
876
				*(ptr++)=hclr|(bclr<<4);
877
878
				i=3;
				if(bline&BL_HELP) {
879
					*(ptr++)=api->chars->button_left;
880
					*(ptr++)=hclr|(bclr<<4);
881
					*(ptr++)=api->chars->help_char;
882
					*(ptr++)=lclr|(bclr<<4);
883
					*(ptr++)=api->chars->button_right;
884
					*(ptr++)=hclr|(bclr<<4);
885
886
					i+=3;
				}
887
888
889
890
891
892
893
894
895
896
				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++) {
897
				*(ptr++)=api->chars->list_top;
898
				*(ptr++)=hclr|(bclr<<4);
899
			}
900
			*(ptr++)=api->chars->list_top_right;
901
			*(ptr++)=hclr|(bclr<<4);
902
			*(ptr++)=api->chars->list_left;
903
			*(ptr++)=hclr|(bclr<<4);
904
905
906
907
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
908
				*(ptr++)=hclr|(bclr<<4);
909
910
911
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
912
				*(ptr++)=hclr|(bclr<<4);
913
914
915
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
916
				*(ptr++)=hclr|(bclr<<4);
917
			}
918
			*(ptr++)=api->chars->list_right;
919
			*(ptr++)=hclr|(bclr<<4);
920
			*(ptr++)=api->chars->list_separator_left;
921
			*(ptr++)=hclr|(bclr<<4);
922
			for(i=0;i<width-2;i++) {
923
				*(ptr++)=api->chars->list_horizontal_separator;
924
				*(ptr++)=hclr|(bclr<<4);
925
			}
926
			*(ptr++)=api->chars->list_separator_right;
927
			*(ptr++)=hclr|(bclr<<4);
928
929
930
931
932
933
		}

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

		if(!bar) {
934
935
936
937
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
938
			i=0;
939
940
941
942
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
943
944
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
945
			if((*cur)==opts-1)
946
947
948
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
949
950
951
952
953
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
954
955
956
957
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
958
959
				(*cur)=i+(*bar);
			}
960
961
962
		}
		if((*cur)<0)
			(*cur)=0;
963

964
965
966
		j=0;
		if(i<0) i=0;
		longopt=0;
967
		while(j<height-vbrdrsize) {
968
			if(!(mode&WIN_NOBRDR)) {
969
				*(ptr++)=api->chars->list_left;
970
				*(ptr++)=hclr|(bclr<<4);
971
972
			}
			*(ptr++)=' ';
973
			*(ptr++)=hclr|(bclr<<4);
974
			*(ptr++)=api->chars->list_scrollbar_separator;
975
			*(ptr++)=lclr|(bclr<<4);
976
			if(i==(*cur))
977
				a=lbclr;
978
			else
979
				a=lclr|(bclr<<4);
980
			if(i<opts && option[i]!=NULL) {
981
982
983
984
985
986
987
988
989
				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; 
				}
990
			}
991
992
			else
				c=0;
993
			while(c<width-hbrdrsize-2) {
994
				*(ptr++)=' ';
995
996
997
998
				*(ptr++)=a;
				c++;
			}
			if(!(mode&WIN_NOBRDR)) {
999
				*(ptr++)=api->chars->list_right;
1000
				*(ptr++)=hclr|(bclr<<4);
1001
			}
1002
1003
1004
1005
			i++;
			j++; 
		}
		if(!(mode&WIN_NOBRDR)) {
1006
			*(ptr++)=api->chars->list_bottom_left;
1007
			*(ptr++)=hclr|(bclr<<4);
1008
			for(i=0;i<width-2;i++) {
1009
				*(ptr++)=api->chars->list_bottom;
1010
				*(ptr++)=hclr|(bclr<<4); 
1011
			}
1012
			*(ptr++)=api->chars->list_bottom_right;
1013
			*(ptr)=hclr|(bclr<<4);	/* Not incremented to shut ot BCC */
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
		}
		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);
1024
			textattr(lclr|(bclr<<4));
1025
			putch(api->chars->down_arrow);	   /* put down arrow */
1026
			textattr(hclr|(bclr<<4)); 
1027
		}
1028

1029
1030
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
1031
			textattr(lclr|(bclr<<4));
1032
			putch(api->chars->up_arrow);	   /* put the up arrow */
1033
			textattr(hclr|(bclr<<4)); 
1034
		}
1035

1036
1037
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
1038
			if(bclr==BLUE) {
1039
1040
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
1041
1042
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
1043
1044
1045
1046
				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);
1047
1048
				for(i=1;i<width*2;i+=2)
					shade[i]=DARKGRAY;
1049
1050
				puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
1051
			}
1052
		}