uifc32.c 80.7 KB
Newer Older
1
/* Curses implementation of UIFC (user interface) library based on uifc.c */
2
// vi: tabstop=4
3

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

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

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

#define BLINK	128

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    api=uifcapi;
265
    if (api->chars == NULL) {
266
		switch(getfont(1)) {
267
			case -1:
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
			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;
		}
	}
285

286
    /* install function handlers */
287
288
289
290
291
292
293
294
    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
295
	api->showbuf=showbuf;
296
	api->timedisplay=timedisplay;
297
	api->bottomline=bottomline;
298
	api->getstrxy=ugetstr;
299
	api->printf=uprintf;
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
329

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

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

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

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

353
    if(txtinfo.screenwidth<40) {
354
355
		uifcbail();
        printf("\r\nUIFC: Screen width (%u) must be at least 40 characters\r\n"
356
357
358
359
360
361
362
            ,txtinfo.screenwidth);
        return(-3);
    }
	api->scrn_width=txtinfo.screenwidth;

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

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

    cursor=_NOCURSOR;
    _setcursortype(cursor);

409
	if(cio_api.mouse && !(api->mode&UIFC_NOMOUSE)) {
410
		api->mode|=UIFC_MOUSE;
411
412
		uifc_mouse_enable();
	}
413

414
415
416
417
418
419
420
	/* 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;

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

	api->initialized=TRUE;
426

427
    return(0);
428
429
}

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

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

542
543
void uifcbail(void)
{
deuce's avatar
deuce committed
544
545
	int i;

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

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

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

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

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

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

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

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

664
665
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
666
667

	title_len=strlen(title);
668
669
670

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1035
1036