uifc32.c 80.2 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
static size_t blk_scrn_len;
61
62
63
static struct vmem_cell *blk_scrn;
static struct vmem_cell *tmp_buffer;
static struct vmem_cell *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
/****************************************************************************/
/* Initialization function, see uifc.h for details.							*/
/* Returns 0 on success.													*/
/****************************************************************************/

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

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

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

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

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

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

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

    api=uifcapi;
211
212
    if (api->chars == NULL)
	api->chars = &cp437_chars;
213

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

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

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

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

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

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

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

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

    cursor=_NOCURSOR;
    _setcursortype(cursor);

339
	if(cio_api.mouse && !(api->mode&UIFC_NOMOUSE)) {
340
		api->mode|=UIFC_MOUSE;
341
342
		uifc_mouse_enable();
	}
343

344
345
346
347
348
349
350
	/* 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;

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

	api->initialized=TRUE;
356

357
    return(0);
358
359
}

360
361
362
363
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
364
365
366
	struct ciolib_screen *screen;
	struct ciolib_screen *sbuffer;
	int sbufsize;
367
368
369
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;
370
371
372
373
374
375
376
377
378
379
380
381
	uint32_t bg0;
	uint32_t bg1;
	uint32_t bg6;
	uint32_t fg8;

	attr2palette(0x08, &fg8, &bg0);
	attr2palette(0x10, NULL, &bg1);
	attr2palette(0x60, NULL, &bg6);
	screen=ciolib_savescreen();
	sbuffer=ciolib_savescreen();
	FREE_AND_NULL(sbuffer->pixels);
	sbufsize=screen->text_info.screenwidth * screen->text_info.screenheight * sizeof(screen->vmem[0]);
382
383
	while(1) {
		key=getch();
384
		if(key==0 || key==0xe0)
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
			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:
407
						memcpy(sbuffer->vmem,screen->vmem,sbufsize);
408
409
410
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
411
412
413

								if (sbuffer->vmem[pos].bg != bg1)
									sbuffer->vmem[pos].bg = bg1;
414
								else
415
416
417
418
419
420
421
422
423
									sbuffer->vmem[pos].bg = bg6;
								if (sbuffer->vmem[pos].bg == sbuffer->vmem[pos].fg)
									attr2palette(sbuffer->vmem[pos].legacy_attr | 0x08, &sbuffer->vmem[pos].fg, &sbuffer->vmem[pos].bg);
								if ((sbuffer->vmem[pos].legacy_attr & 0x70) != 0x10)
									sbuffer->vmem[pos].legacy_attr = (sbuffer->vmem[pos].legacy_attr & 0x8f) | 0x10;
								else
									sbuffer->vmem[pos].legacy_attr = (sbuffer->vmem[pos].legacy_attr & 0x8f) | 0x60;
								if (((sbuffer->vmem[pos].legacy_attr & 0x70) >> 4) == (sbuffer->vmem[pos].legacy_attr & 0x08))
									sbuffer->vmem[pos].legacy_attr |= 0x08;
424
425
							}
						}
426
						restorescreen(sbuffer);
427
428
429
						break;
					case CIOLIB_BUTTON_1_DRAG_END:
						lines=abs(mevent.endy-mevent.starty)+1;
430
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
431
432
433
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
434
								copybuf[outpos++]=screen->vmem[(y*api->scrn_width+x)].ch ? screen->vmem[(y*api->scrn_width+x)].ch : ' ';
435
							}
436
437
438
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
439
440
441
442
							copybuf[outpos++]='\n';
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
443
444
445
						restorescreen(screen);
						freescreen(screen);
						freescreen(sbuffer);
446
447
448
449
						return;
				}
				break;
			default:
450
451
452
				restorescreen(screen);
				freescreen(screen);
				freescreen(sbuffer);
453
454
455
456
457
458
				ungetch(key);
				return;
		}
	}
}

459
460
461
462
463
464
465
466
467
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);
468
469
470
471
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
		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);
}

489
490
void uifcbail(void)
{
deuce's avatar
deuce committed
491
492
	int i;

493
494
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
495
	uifc_mouse_disable();
496
	suspendciolib();
deuce's avatar
deuce committed
497
498
499
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
500
	api->initialized=FALSE;
deuce's avatar
deuce committed
501
502
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
503
504
505
506
507
508
509
510
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
511
    textattr(api->bclr|(api->cclr<<4));
512
    gotoxy(1,1);
513
    clreol();
514
515
    gotoxy(3,1);
	cputs(str);
516
    if(!vmem_puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn))
517
518
        return(-1);
    gotoxy(1,api->scrn_len+1);
519
    clreol();
520
	reset_dynamic();
521
	setname(str);
522
523
524
525
526
527
528
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
529
	vmem_gettext(x1,y1,x2,y2,tmp_buffer2);
530
	if(down)
531
		vmem_puttext(x1,y1+1,x2,y2,tmp_buffer2);
532
	else
533
		vmem_puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)));
534
535
536
537
538
539
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
540
static void timedisplay(BOOL force)
541
542
543
544
545
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
546
	if(force || difftime(now,savetime)>=60) {
547
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
548
		savetime=now;
549
550
551
552
553
554
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
555
static void truncspctrl(char *str)
556
557
558
559
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
560
	while(c && (uchar)str[c-1]<=' ') c--;
561
562
563
564
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

565
static void
566
inactive_win(struct vmem_cell *buf, int left, int top, int right, int bottom, int y, int hbrdrsize, uchar cclr, uchar lclr, uchar hclr, int btop)
567
568
569
570
571
572
573
574
{
	int width = right - left + 1;
	int height = bottom - top + 1;
	int i, j;

	vmem_gettext(left, top, right, bottom, buf);
	for (i=0; i < (width * height); i++)
		set_vmem_attr(&buf[i], lclr | (cclr<<4));
575
576
	j=(((y-btop)*width))+3+((width-hbrdrsize-2));
	for(i=(((y-btop)*width))+3;i<j;i++)
577
578
579
580
581
		set_vmem_attr(&buf[i], hclr|(cclr<<4));

	vmem_puttext(left, top, right, bottom, buf);
}

582
583
584
585
/****************************************************************************/
/* 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
586
	, char *initial_title, char **option)
587
{
deuce's avatar
deuce committed
588
	struct vmem_cell *ptr, *win, shade[MAX_LINES*2], line[MAX_COLS];
589
	char search[MAX_OPLN];
590
591
592
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
593
594
595
596
597
598
599
600
601
602
	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;
603
	int title_len;
604
	int tmpcur=0;
605
	struct mouse_event mevnt;
606
	char	*title=NULL;
607
	int	a,b,c,longopt;
608
	int	optheight=0;
609
	int gotkey;
610
611
	uchar	hclr,lclr,bclr,cclr,lbclr;

612
	if(cur==NULL) cur=&tmpcur;
613
	api->exit_flags = 0;
614
615
616
617
618
619
620
621
622
623
624
625
	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;
	}
626
627
	title=strdup(initial_title==NULL?"":initial_title);

628
629
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
630
631

	title_len=strlen(title);
632
633
634

	if(mode&WIN_FAT) {
		s_top=1;
635
		s_left=2;
636
637
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
638
	}
639
640
641
642
643
644
645
646
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}
647
648
649
650
651
652
653
654
	/* 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++;
655

656
	/* Sanity-check the savnum */
657
658
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
659

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

662
	/* Create the status bar/bottom-line */
663
	int bline = mode;
664
665
	if (api->bottomline != NULL) {
		if ((mode&(WIN_XTR | WIN_PASTEXTR)) == WIN_XTR && (*cur) == opts - 1)
666
			api->bottomline(bline & ~WIN_PASTE);
667
668
669
		else
			api->bottomline(bline);
	}
670
	optheight=opts+vbrdrsize;
671
	height=optheight;
672
673
674
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
675
676
	if(top+height>s_bottom)
		height=(s_bottom)-top;
677
678
	if(optheight>height)
		optheight=height;
679
680
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
681
		for(i=0;i<opts;i++) {
682
			if(option[i]!=NULL) {
683
				truncspctrl(option[i]);
684
685
686
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
687
688
		}
	}
689
	/* Determine minimum widths here to accommodate mouse "icons" in border */
690
	if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) {
691
		if(api->help_available && width<8)
692
693
694
695
			width=8;
		else if(width<5)
			width=5;
	}
696
697
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
698
699
700
701
702
		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;
703
			title_len=strlen(title);
704
		}
705
	}
706
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
707
		left=(s_right-s_left-width+1)/2;
708
	else if(mode&WIN_RHT)
709
		left=s_right-(width+hbrdrsize+2+left);
710
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
711
		top=(api->scrn_len-height+1)/2-2;
712
	else if(mode&WIN_BOT)
713
		top=s_bottom-height-top;
714
715
	if(left<0)
		left=0;
716
717
	if(top<0)
		top=0;
718
719

	/* Dynamic Menus */
720
	if(mode&WIN_DYN
721
722
723
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
724
725
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
726
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
727
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
728
		is_redraw=1;
729
730
	}

731
732
733
734
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
735

deuce's avatar
deuce committed
736
737
738
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
739
		api->savnum=0;
deuce's avatar
deuce committed
740
741
	}

742
	if(mode&WIN_SAV) {
743
744
745
746
747
748
		/* 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--) {
749
						/* Restore old screens */
deuce's avatar
deuce committed
750
751
						vmem_puttext(sav[j].left,sav[j].top,sav[j].right,sav[j].bot
							,(void *)sav[j].buf);	/* put original window back */
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
						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 */
deuce's avatar
deuce committed
767
768
					vmem_puttext(sav[api->savnum].left,sav[api->savnum].top,sav[api->savnum].right,sav[api->savnum].bot
						,(void *)sav[api->savnum].buf);	/* put original window back */
769
					FREE_AND_NULL(sav[api->savnum].buf);
deuce's avatar
deuce committed
770
					if ((sav[api->savnum].buf = malloc((width + 3) * (height + 2) * sizeof(struct vmem_cell)))==NULL) {
771
772
773
						cprintf("UIFC line %d: error allocating %u bytes."
							,__LINE__,(width+3)*(height+2)*2);
						free(title);
774
775
						if(!(api->mode&UIFC_NHM))
							uifc_mouse_enable();
776
777
						return(-1);
					}
deuce's avatar
deuce committed
778
779
					vmem_gettext(s_left+left,s_top+top,s_left+left+width+1
						,s_top+top+height,(void *)sav[api->savnum].buf);	  /* save again */
780
781
782
783
784
785
786
787
788
789
790
791
					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++;
792
793
			}
		}
794
		else {
deuce's avatar
deuce committed
795
			if((sav[api->savnum].buf=malloc((width+3)*(height+2)*sizeof(struct vmem_cell)))==NULL) {
796
797
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
798
				free(title);
799
800
				if(!(api->mode&UIFC_NHM))
					uifc_mouse_enable();
801
				return(-1);
802
			}
deuce's avatar
deuce committed
803
804
			vmem_gettext(s_left+left,s_top+top,s_left+left+width+1
				,s_top+top+height,(void *)sav[api->savnum].buf);
805
806
807
808
			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;
809
810
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
811
		}
812
813
814
815
816
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
817
				vmem_puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
818
			if((unsigned)(s_top+height+top)<=api->scrn_len)
819
				vmem_puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
820
			if(left)
821
				vmem_puttext(1,s_top+top,s_left+left-1,s_top+height+top
822
					,blk_scrn);
823
			if(s_left+left+width<=s_right)
824
				vmem_puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
825
					,s_top+height+top,blk_scrn);
826
		}
deuce's avatar
deuce committed
827
		ptr=tmp_buffer;
828
		if(!(mode&WIN_NOBRDR)) {
829
			set_vmem(ptr++, api->chars->list_top_left, hclr|(bclr<<4), 0);
830

831
			if(api->mode&UIFC_MOUSE) {
832
				set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0);
833
				/* *(ptr++)=''; */
834
835
				set_vmem(ptr++, api->chars->close_char, lclr|(bclr<<4), 0);
				set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0);
836
				i=3;
837
				if(api->help_available) {
838
839
840
					set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0);
					set_vmem(ptr++, api->chars->help_char, lclr|(bclr<<4), 0);
					set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0);
841
842
					i+=3;
				}
843
844
845
846
847
848
849
850
851
				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;

852
853
854
855
			for(;i<width-2;i++)
				set_vmem(ptr++, api->chars->list_top, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_top_right, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_left, hclr|(bclr<<4), 0);
856
857
858
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
859
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
860
			}
861
862
863
864
865
866
867
868
869
			for(i=0;i<a;i++)
				set_vmem(ptr++, title[i], hclr|(bclr<<4), 0);
			for(i=0;i<width-(a+b)-2;i++)
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_right, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_separator_left, hclr|(bclr<<4), 0);
			for(i=0;i<width-2;i++)
				set_vmem(ptr++, api->chars->list_horizontal_separator, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_separator_right, hclr|(bclr<<4), 0);
870
871
872
873
874
875
		}

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

		if(!bar) {
876
877
878
879
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
880
			i=0;
881
882
883
884
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
885
886
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
887
			if((*cur)==opts-1)
888
889
890
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
891
892
893
894
895
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
896
897
898
899
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
900
901
				(*cur)=i+(*bar);
			}
902
903
904
		}
		if((*cur)<0)
			(*cur)=0;
905

906
907
908
		j=0;
		if(i<0) i=0;
		longopt=0;
909
		while(j<height-vbrdrsize) {
910
911
912
913
			if(!(mode&WIN_NOBRDR))
				set_vmem(ptr++, api->chars->list_left, hclr|(bclr<<4), 0);
			set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_scrollbar_separator, lclr|(bclr<<4), 0);
914
			if(i==(*cur))
915
				a=lbclr;
916
			else
917
				a=lclr|(bclr<<4);
918
			if(i<opts && option[i]!=NULL) {
919
920
921
922
923
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
924
925
				for(c=0;c<b;c++)
					set_vmem(ptr++, option[i][c], a, 0);
926
			}
927
928
			else
				c=0;
929
			while(c<width-hbrdrsize-2) {
930
				set_vmem(ptr++, ' ', a, 0);
931
932
				c++;
			}
933
934
			if(!(mode&WIN_NOBRDR))
				set_vmem(ptr++, api->chars->list_right, hclr|(bclr<<4), 0);
935
			i++;
936
			j++;
937
938
		}
		if(!(mode&WIN_NOBRDR)) {
939
940
941
942
			set_vmem(ptr++, api->chars->list_bottom_left, hclr|(bclr<<4), 0);
			for(i=0;i<width-2;i++)
				set_vmem(ptr++, api->chars->list_bottom, hclr|(bclr<<4), 0);
			set_vmem(ptr, api->chars->list_bottom_right, hclr|(bclr<<4), 0);	/* Not incremented to shut up BCC */
943
		}
944
		vmem_puttext(s_left+left,s_top+top,s_left+left+width-1
945
946
947
948
949
950
951
952
			,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);
953
			textattr(lclr|(bclr<<4));
954
			putch(api->chars->down_arrow);	   /* put down arrow */
955
			textattr(hclr|(bclr<<4));
956
		}
957

958
959
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
960
			textattr(lclr|(bclr<<4));
961
			putch(api->chars->up_arrow);	   /* put the up arrow */
962
			textattr(hclr|(bclr<<4));
963
		}
964

965
966
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
967
			if(bclr==BLUE) {
deuce's avatar
deuce committed
968
				vmem_gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
969
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
970
971
972
				for(i=0;i<height*2;i++)
					set_vmem_attr(&shade[i], DARKGRAY);
				vmem_puttext(s_left+left+width,s_top+top+1,s_left+left+width+1
973
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
974
				vmem_gettext(s_left+left+2,s_top+top+height,s_left+left+width+1
975
					,s_top+top+height,shade);
deuce's avatar
deuce committed
976
977
978
				for(i=0;i<width;i++)
					set_vmem_attr(&shade[i], DARKGRAY);
				vmem_puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
979
					,s_top+top+height,shade);
980
			}
981
		}
982
983
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
984
985
986
987
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
988
		i=(*cur)+(top+tbrdrwidth-y);
989
		j=tbrdrwidth-1;
990
991

		longopt=0;
992
		while(j<height-bbrdrwidth-1) {
deuce's avatar
deuce committed
993
			ptr=tmp_buffer;
994
			if(i==(*cur))
995
				a=lbclr;
996
			else
997
				a=lclr|(bclr<<4);
998
			if(i<opts && option[i]!=NULL) {
999
1000
1001
1002
1003
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
1004
1005
				for(c=0;c<b;c++)
					set_vmem(ptr++, option[i][c], a, 0);
1006
			}
1007
1008
			else
				c=0;
1009
			while(c<width-hbrdrsize-2) {
1010
				set_vmem(ptr++, ' ', a, 0);
1011
				c++;
1012
1013
			}
			i++;
1014
			j++;
1015
			vmem_puttext(s_left+left+lbrdrwidth+2,s_top+top+j,s_left+left+width-rbrdrwidth-1
1016
				,s_top+top+j,tmp_buffer);
1017
1018
		}
	}
1019
	free(title);
1020
1021
1022

	last_menu_cur=cur;
	last_menu_bar=bar;
1023
1024
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_enable();
1025

1026
	if(mode&WIN_IMM) {
1027
		return(-2);
1028
	}
1029