uifc32.c 79.3 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)*2));
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
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
static void
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 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));
	j=(((y-top)*width))+3+((width-hbrdrsize-2));
	for(i=(((y-top)*width))+3;i<j;i++)
		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
{
588
589
	uchar line[MAX_COLS*2],shade[MAX_LINES*4];
	struct vmem_cell *ptr, *win;
590
    char search[MAX_OPLN];
591
592
593
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
594
595
596
597
598
599
600
601
602
603
	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;
604
	int title_len;
605
	int tmpcur=0;
606
	struct mouse_event mevnt;
607
	char	*title=NULL;
608
	int	a,b,c,longopt;
609
	int	optheight=0;
610
	int gotkey;
611
612
	uchar	hclr,lclr,bclr,cclr,lbclr;

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

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

	title_len=strlen(title);
633
634
635

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

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

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

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

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

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

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

743
	if(mode&WIN_SAV) {
744
745
746
747
748
749
		/* 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--) {
750
						/* Restore old screens */
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
						puttext(sav[j].left,sav[j].top,sav[j].right,sav[j].bot
							,sav[j].buf);	/* put original window back */
						FREE_AND_NULL(sav[j].buf);
					}
					api->savnum=i;
				}
			}
		}
		/* savnum not the next one - must be a dynamic window or we popped back up the stack */
		if(sav[api->savnum].buf != NULL) {
			/* Is this even the right window? */
			if(sav[api->savnum].cur==cur
					&& sav[api->savnum].bar==bar) {
				if((sav[api->savnum].left!=s_left+left
					|| sav[api->savnum].top!=s_top+top
					|| sav[api->savnum].right!=s_left+left+width+1
					|| sav[api->savnum].bot!=s_top+top+height)) { /* dimensions have changed */
					puttext(sav[api->savnum].left,sav[api->savnum].top,sav[api->savnum].right,sav[api->savnum].bot
						,sav[api->savnum].buf);	/* put original window back */
					FREE_AND_NULL(sav[api->savnum].buf);
771
					if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
772
773
774
						cprintf("UIFC line %d: error allocating %u bytes."
							,__LINE__,(width+3)*(height+2)*2);
						free(title);
775
776
						if(!(api->mode&UIFC_NHM))
							uifc_mouse_enable();
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
						return(-1);
					}
					gettext(s_left+left,s_top+top,s_left+left+width+1
						,s_top+top+height,sav[api->savnum].buf);	  /* save again */
					sav[api->savnum].left=s_left+left;
					sav[api->savnum].top=s_top+top;
					sav[api->savnum].right=s_left+left+width+1;
					sav[api->savnum].bot=s_top+top+height;
					sav[api->savnum].cur=cur;
					sav[api->savnum].bar=bar;
				}
			}
			else {
				/* Find something available... */
				while(sav[api->savnum].buf!=NULL)
					api->savnum++;
793
794
			}
		}
795
		else {
796
			if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
797
798
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
799
				free(title);
800
801
				if(!(api->mode&UIFC_NHM))
					uifc_mouse_enable();
802
				return(-1);
803
			}
804
			gettext(s_left+left,s_top+top,s_left+left+width+1
805
				,s_top+top+height,sav[api->savnum].buf);
806
807
808
809
			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;
810
811
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
812
		}
813
814
815
816
817
	}

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

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

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

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

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

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

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

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

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

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

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

1031
1032
1033
1034
	if(mode&WIN_ORG) {
		if(api->timedisplay != NULL)
			api->timedisplay(/* force? */TRUE);
	}
1035

1036
1037
	while(1) {
	#if 0					/* debug */
1038
1039
		struct text_info txtinfo;
		gettextinfo(&txtinfo);
1040
		gotoxy(30,1);
1041
1042
		cprintf("y=%2d h=%2d c=%2d b=%2d s=%2d o=%2d w=%d/%d h=%d/%d"
			,y,height,*cur,bar ? *bar :0xff,api->savnum,opts,txtinfo.screenwidth, api->scrn_width, txtinfo.screenheight, api->scrn_len);
1043
	#endif