uifc32.c 80.4 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;
deuce's avatar
deuce committed
430
						copybuf=malloc((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));
deuce's avatar
deuce committed
443
						free(copybuf);
444
445
446
						restorescreen(screen);
						freescreen(screen);
						freescreen(sbuffer);
447
448
449
450
						return;
				}
				break;
			default:
451
452
453
				restorescreen(screen);
				freescreen(screen);
				freescreen(sbuffer);
454
455
456
457
458
459
				ungetch(key);
				return;
		}
	}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	title_len=strlen(title);
637
638
639

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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