uifc32.c 79.3 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,*win;
622
    char search[MAX_OPLN];
623
624
625
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
626
627
628
629
630
631
632
633
634
635
	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;
636
	int title_len;
637
	int tmpcur=0;
638
	struct mouse_event mevnt;
639
	char	*title=NULL;
640
	int	a,b,c,longopt;
641
	int	optheight=0;
642
	int gotkey;
643
644
	uchar	hclr,lclr,bclr,cclr,lbclr;

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

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

	title_len=strlen(title);
665
666
667

	if(mode&WIN_FAT) {
		s_top=1;
668
		s_left=2;
669
670
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
671
	}
672
673
674
675
676
677
678
679
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}
680
681
682
683
684
685
686
687
	/* 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++;
688

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

693
694
	api->help_available = (api->helpbuf!=NULL || api->helpixbfile[0]!=0);

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

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

764
765
766
767
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
768

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

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

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

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

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

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

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

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

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