uifc32.c 80.5 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
#include <datewrap.h>	// localtime_r()
51

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

#define BLINK	128

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

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

/* API routines */
static void uifcbail(void);
static int  uscrn(char *str);
static int  ulist(int mode, int left, int top, int width, int *dflt, int *bar
	,char *title, char **option);
static int  uinput(int imode, int left, int top, char *prompt, char *str
	,int len ,int kmode);
static void umsg(char *str);
static void upop(char *str);
static void sethelp(int line, char* file);
static void showbuf(int mode, int left, int top, int width, int height, char *title
	, char *hbuf, int *curp, int *barp);

/* Dynamic menu support */
static int *last_menu_cur=NULL;
static int *last_menu_bar=NULL;
static int save_menu_cur=-1;
static int save_menu_bar=-1;
94
static int save_menu_opts=-1;
95

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

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

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

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

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

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

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

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

165
166
void uifc_mouse_enable(void)
{
167
168
169
170
171
	ciomouse_setevents(0);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END);
	ciomouse_addevent(CIOLIB_BUTTON_1_CLICK);
deuce's avatar
deuce committed
172
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
173
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
174
175
176
177
178
179
180
181
182
	showmouse();
}

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

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

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

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

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

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

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

215
    /* install function handlers */
216
217
218
219
220
221
222
223
    api->bail=uifcbail;
    api->scrn=uscrn;
    api->msg=umsg;
    api->pop=upop;
    api->list=ulist;
    api->input=uinput;
    api->sethelp=sethelp;
    api->showhelp=help;
deuce's avatar
deuce committed
224
	api->showbuf=showbuf;
225
	api->timedisplay=timedisplay;
226
	api->bottomline=bottomline;
227
	api->getstrxy=ugetstr;
228
	api->printf=uprintf;
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

    if(api->scrn_len!=0) {
        switch(api->scrn_len) {
            case 14:
                textmode(C80X14);
                break;
            case 21:
                textmode(C80X21);
                break;
            case 25:
                textmode(C80);
                break;
            case 28:
                textmode(C80X28);
                break;
            case 43:
                textmode(C80X43);
                break;
            case 50:
                textmode(C80X50);
                break;
            case 60:
                textmode(C80X60);
                break;
            default:
                textmode(C4350);
                break;
        }
    }

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

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

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

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

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

314
315
	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
316
				cprintf("UIFC line %d: error allocating %u bytes."
317
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
318
319
				return(-1);
	}
320
	if((tmp_buffer=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
321
				cprintf("UIFC line %d: error allocating %u bytes."
322
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
323
324
				return(-1);
	}
325
	if((tmp_buffer2=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
326
				cprintf("UIFC line %d: error allocating %u bytes."
327
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
328
329
				return(-1);
	}
330
331
332
333
334
335
    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);
	}
336
337
338
339

    cursor=_NOCURSOR;
    _setcursortype(cursor);

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

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

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

	api->initialized=TRUE;
357

358
    return(0);
359
360
}

361
362
363
364
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
365
366
367
	struct ciolib_screen *screen;
	struct ciolib_screen *sbuffer;
	int sbufsize;
368
369
370
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;
371
372
373
374
375
376
377
378
379
380
381
382
	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]);
383
384
	while(1) {
		key=getch();
385
		if(key==0 || key==0xe0)
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
			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:
408
						memcpy(sbuffer->vmem,screen->vmem,sbufsize);
409
410
411
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
412
413
414

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

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

491
492
void uifcbail(void)
{
deuce's avatar
deuce committed
493
494
	int i;

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

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

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

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
542
static void timedisplay(BOOL force)
543
544
{
	static time_t savetime;
545
	static int savemin;
546
	time_t now;
547
	struct tm gm;
548
549

	now=time(NULL);
550
551
	localtime_r(&now, &gm);
	if(force || savemin != gm.tm_min || difftime(now,savetime)>=60) {
552
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
553
		savetime=now;
554
		savemin = gm.tm_min;
555
556
557
558
559
560
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
561
static void truncspctrl(char *str)
562
563
564
565
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
566
	while(c && (uchar)str[c-1]<=' ') c--;
567
568
569
570
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

571
static void
572
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)
573
574
575
576
577
578
579
580
{
	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));
581
582
	j=(((y-btop)*width))+3+((width-hbrdrsize-2));
	for(i=(((y-btop)*width))+3;i<j;i++)
583
584
585
586
587
		set_vmem_attr(&buf[i], hclr|(cclr<<4));

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

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

618
	if(cur==NULL) cur=&tmpcur;
619
	api->exit_flags = 0;
620
621
622
623
624
625
626
627
628
629
630
631
	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;
	}
632
633
	title=strdup(initial_title==NULL?"":initial_title);

634
635
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
636
637

	title_len=strlen(title);
638
639
640

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

662
	/* Sanity-check the savnum */
663
664
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
665

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

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

	/* Dynamic Menus */
726
	if(mode&WIN_DYN
727
728
729
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
730
731
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
732
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
733
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
734
		is_redraw=1;
735
736
	}

737
738
739
740
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
741

deuce's avatar
deuce committed
742
743
744
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
745
		api->savnum=0;
deuce's avatar
deuce committed
746
747
	}

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

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

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

858
859
860
861
			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);
862
863
864
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
865
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
866
			}
867
868
869
870
871
872
873
874
875
			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);
876
877
878
879
880
881
		}

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

		if(!bar) {
882
883
884
885
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
886
			i=0;
887
888
889
890
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
891
892
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
893
			if((*cur)==opts-1)
894
895
896
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
897
898
899
900
901
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
902
903
904
905
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
906
907
				(*cur)=i+(*bar);
			}
908
909
910
		}
		if((*cur)<0)
			(*cur)=0;
911

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

964
965
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
966
			textattr(lclr|(bclr<<4));
967
			putch(api->chars->up_arrow);	   /* put the up arrow */
968
			textattr(hclr|(bclr<<4));
969
		}
970

971
972
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
973
			if(bclr==BLUE) {
deuce's avatar
deuce committed
974
				vmem_gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
975
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
976
977
978
				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
979
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
980
				vmem_gettext(s_left+left+2,s_top+top+height,s_left+left+width+1
981
					,s_top+top+height,shade);
deuce's avatar
deuce committed
982
983
984
				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
985
					,s_top+top+height,shade);
986
			}
987
		}
988
989
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
990
991
992
993
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
994
		i=(*cur)+(top+tbrdrwidth-y);
995
		j=tbrdrwidth-1;
996
997

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