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

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
static uifc_graphics_t ascii_chars = {
	.background='#',
	.help_char='?',
	.close_char='X',
	.up_arrow='^',
164
	.down_arrow='v',
165
166
167
	.button_left='[',
	.button_right=']',

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

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

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

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

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

218
219
void uifc_mouse_enable(void)
{
220
221
222
223
224
	ciomouse_setevents(0);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END);
	ciomouse_addevent(CIOLIB_BUTTON_1_CLICK);
deuce's avatar
deuce committed
225
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
226
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
227
228
229
230
231
232
233
234
235
	showmouse();
}

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

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

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

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

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

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

    api=uifcapi;
265
    if (api->chars == NULL) {
266
		switch(getfont(1)) {
267
			case -1:
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
			case 0:
			case 17:
			case 18:
			case 19:
			case 25:
			case 26:
			case 27:
			case 28:
			case 29:
			case 31:
				api->chars = &cp437_chars;
				break;
			default:
				api->chars = &ascii_chars;
				break;
		}
	}
285

286
    /* install function handlers */
287
288
289
290
291
292
293
294
    api->bail=uifcbail;
    api->scrn=uscrn;
    api->msg=umsg;
    api->pop=upop;
    api->list=ulist;
    api->input=uinput;
    api->sethelp=sethelp;
    api->showhelp=help;
deuce's avatar
deuce committed
295
	api->showbuf=showbuf;
296
	api->timedisplay=timedisplay;
297
	api->bottomline=bottomline;
298
	api->getstrxy=ugetstr;
299
	api->printf=uprintf;
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

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

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

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

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

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

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
363
364
365
366
367
368
            || txtinfo.currmode==MONO || txtinfo.currmode==BW40 || txtinfo.currmode==BW80
            || txtinfo.currmode==MONO14 || txtinfo.currmode==BW40X14 || txtinfo.currmode==BW80X14
            || txtinfo.currmode==MONO21 || txtinfo.currmode==BW40X21 || txtinfo.currmode==BW80X21
            || txtinfo.currmode==MONO28 || txtinfo.currmode==BW40X28 || txtinfo.currmode==BW80X28
            || txtinfo.currmode==MONO43 || txtinfo.currmode==BW40X43 || txtinfo.currmode==BW80X43
            || txtinfo.currmode==MONO50 || txtinfo.currmode==BW40X50 || txtinfo.currmode==BW80X50
deuce's avatar
deuce committed
369
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
370
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
371
	{
372
373
374
375
376
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
377
    } else {
378
379
380
381
382
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
383
    }
384

385
386
	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
387
				cprintf("UIFC line %d: error allocating %u bytes."
388
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
389
390
				return(-1);
	}
391
	if((tmp_buffer=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
392
				cprintf("UIFC line %d: error allocating %u bytes."
393
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
394
395
				return(-1);
	}
396
	if((tmp_buffer2=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
397
				cprintf("UIFC line %d: error allocating %u bytes."
398
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
399
400
				return(-1);
	}
401
402
403
404
405
406
    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);
	}
407
408
409
410

    cursor=_NOCURSOR;
    _setcursortype(cursor);

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

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

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

	api->initialized=TRUE;
428

429
    return(0);
430
431
}

432
433
434
435
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
436
437
438
	struct ciolib_screen *screen;
	struct ciolib_screen *sbuffer;
	int sbufsize;
439
440
441
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;
442
443
444
445
446
447
448
449
450
451
452
453
	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]);
454
455
	while(1) {
		key=getch();
456
		if(key==0 || key==0xe0)
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
			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:
479
						memcpy(sbuffer->vmem,screen->vmem,sbufsize);
480
481
482
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
483
484
485

								if (sbuffer->vmem[pos].bg != bg1)
									sbuffer->vmem[pos].bg = bg1;
486
								else
487
488
489
490
491
492
493
494
495
									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;
496
497
							}
						}
498
						restorescreen(sbuffer);
499
500
501
						break;
					case CIOLIB_BUTTON_1_DRAG_END:
						lines=abs(mevent.endy-mevent.starty)+1;
502
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
503
504
505
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
506
								copybuf[outpos++]=screen->vmem[(y*api->scrn_width+x)].ch ? screen->vmem[(y*api->scrn_width+x)].ch : ' ';
507
							}
508
509
510
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
511
512
513
514
							copybuf[outpos++]='\n';
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
515
516
517
						restorescreen(screen);
						freescreen(screen);
						freescreen(sbuffer);
518
519
520
521
						return;
				}
				break;
			default:
522
523
524
				restorescreen(screen);
				freescreen(screen);
				freescreen(sbuffer);
525
526
527
528
529
530
				ungetch(key);
				return;
		}
	}
}

531
532
533
534
535
536
537
538
539
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);
540
541
542
543
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
		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);
}

561
562
void uifcbail(void)
{
deuce's avatar
deuce committed
563
564
	int i;

565
566
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
567
	uifc_mouse_disable();
568
	suspendciolib();
deuce's avatar
deuce committed
569
570
571
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
572
	api->initialized=FALSE;
deuce's avatar
deuce committed
573
574
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
575
576
577
578
579
580
581
582
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
583
    textattr(api->bclr|(api->cclr<<4));
584
    gotoxy(1,1);
585
    clreol();
586
587
    gotoxy(3,1);
	cputs(str);
588
    if(!vmem_puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn))
589
590
        return(-1);
    gotoxy(1,api->scrn_len+1);
591
    clreol();
592
	reset_dynamic();
593
	setname(str);
594
595
596
597
598
599
600
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
601
	vmem_gettext(x1,y1,x2,y2,tmp_buffer2);
602
	if(down)
603
		vmem_puttext(x1,y1+1,x2,y2,tmp_buffer2);
604
	else
605
		vmem_puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)*2));
606
607
608
609
610
611
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
612
static void timedisplay(BOOL force)
613
614
615
616
617
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
618
	if(force || difftime(now,savetime)>=60) {
619
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
620
		savetime=now;
621
622
623
624
625
626
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
627
static void truncspctrl(char *str)
628
629
630
631
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
632
	while(c && (uchar)str[c-1]<=' ') c--;
633
634
635
636
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
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);
}

654
655
656
657
/****************************************************************************/
/* 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
658
	, char *initial_title, char **option)
659
{
660
661
	uchar line[MAX_COLS*2],shade[MAX_LINES*4];
	struct vmem_cell *ptr, *win;
662
    char search[MAX_OPLN];
663
664
665
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
666
667
668
669
670
671
672
673
674
675
	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;
676
	int title_len;
677
	int tmpcur=0;
678
	struct mouse_event mevnt;
679
	char	*title=NULL;
680
	int	a,b,c,longopt;
681
	int	optheight=0;
682
	int gotkey;
683
684
	uchar	hclr,lclr,bclr,cclr,lbclr;

685
	if(cur==NULL) cur=&tmpcur;
686
	api->exit_flags = 0;
687
688
689
690
691
692
693
694
695
696
697
698
	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;
	}
699
700
	title=strdup(initial_title==NULL?"":initial_title);

701
702
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
703
704

	title_len=strlen(title);
705
706
707

	if(mode&WIN_FAT) {
		s_top=1;
708
		s_left=2;
709
710
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
711
	}
712
713
714
715
716
717
718
719
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}
720
721
722
723
724
725
726
727
	/* 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++;
728

729
	/* Sanity-check the savnum */
730
731
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
732

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

735
	/* Create the status bar/bottom-line */
736
	int bline = mode;
737
738
	if (api->bottomline != NULL) {
		if ((mode&(WIN_XTR | WIN_PASTEXTR)) == WIN_XTR && (*cur) == opts - 1)
739
			api->bottomline(bline & ~WIN_PASTE);
740
741
742
		else
			api->bottomline(bline);
	}
743
	optheight=opts+vbrdrsize;
744
	height=optheight;
745
746
747
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
748
749
	if(top+height>s_bottom)
		height=(s_bottom)-top;
750
751
	if(optheight>height)
		optheight=height;
752
753
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
754
		for(i=0;i<opts;i++) {
755
			if(option[i]!=NULL) {
756
				truncspctrl(option[i]);
757
758
759
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
760
761
		}
	}
762
	/* Determine minimum widths here to accommodate mouse "icons" in border */
763
	if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) {
764
		if(api->help_available && width<8)
765
766
767
768
			width=8;
		else if(width<5)
			width=5;
	}
769
770
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
771
772
773
774
775
		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;
776
			title_len=strlen(title);
777
		}
778
	}
779
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
780
		left=(s_right-s_left-width+1)/2;
781
	else if(mode&WIN_RHT)
782
		left=s_right-(width+hbrdrsize+2+left);
783
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
784
		top=(api->scrn_len-height+1)/2-2;
785
	else if(mode&WIN_BOT)
786
		top=s_bottom-height-top;
787
788
	if(left<0)
		left=0;
789
790
	if(top<0)
		top=0;
791
792

	/* Dynamic Menus */
793
	if(mode&WIN_DYN
794
795
796
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
797
798
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
799
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
800
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
801
		is_redraw=1;
802
803
	}

804
805
806
807
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
808

deuce's avatar
deuce committed
809
810
811
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
812
		api->savnum=0;
deuce's avatar
deuce committed
813
814
	}

815
	if(mode&WIN_SAV) {
816
817
818
819
820
821
		/* 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--) {
822
						/* Restore old screens */
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
						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);
843
					if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
844
845
846
						cprintf("UIFC line %d: error allocating %u bytes."
							,__LINE__,(width+3)*(height+2)*2);
						free(title);
847
848
						if(!(api->mode&UIFC_NHM))
							uifc_mouse_enable();
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
						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++;
865
866
			}
		}
867
		else {
868
			if((sav[api->savnum].buf=malloc((width+3)*(height+2)*2))==NULL) {
869
870
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
871
				free(title);
872
873
				if(!(api->mode&UIFC_NHM))
					uifc_mouse_enable();
874
				return(-1);
875
			}
876
			gettext(s_left+left,s_top+top,s_left+left+width+1
877
				,s_top+top+height,sav[api->savnum].buf);
878
879
880
881
			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;
882
883
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
884
		}
885
886
887
888
889
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
890
				vmem_puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
891
			if((unsigned)(s_top+height+top)<=api->scrn_len)
892
				vmem_puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
893
			if(left)
894
				vmem_puttext(1,s_top+top,s_left+left-1,s_top+height+top
895
					,blk_scrn);
896
			if(s_left+left+width<=s_right)
897
				vmem_puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
898
					,s_top+height+top,blk_scrn);
899
		}
deuce's avatar
deuce committed
900
		ptr=tmp_buffer;
901
		if(!(mode&WIN_NOBRDR)) {
902
			set_vmem(ptr++, api->chars->list_top_left, hclr|(bclr<<4), 0);
903

904
			if(api->mode&UIFC_MOUSE) {
905
				set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0);
906
				/* *(ptr++)=''; */
907
908
				set_vmem(ptr++, api->chars->close_char, lclr|(bclr<<4), 0);
				set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0);
909
				i=3;
910
				if(api->help_available) {
911
912
913
					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);
914
915
					i+=3;
				}
916
917
918
919
920
921
922
923
924
				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;

925
926
927
928
			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);
929
930
931
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
932
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
933
			}
934
935
936
937
938
939
940
941
942
			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);
943
944
945
946
947
948
		}

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

		if(!bar) {
949
950
951
952
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
953
			i=0;
954
955
956
957
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
958
959
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
960
			if((*cur)==opts-1)
961
962
963
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
964
965
966
967
968
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
969
970
971
972
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
973
974
				(*cur)=i+(*bar);
			}
975
976
977
		}
		if((*cur)<0)
			(*cur)=0;
978

979
980
981
		j=0;
		if(i<0) i=0;
		longopt=0;
982
		while(j<height-vbrdrsize) {
983
984
985
986
			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);
987
			if(i==(*cur))
988
				a=lbclr;
989
			else
990
				a=lclr|(bclr<<4);
991
			if(i<opts && option[i]!=NULL) {
992
993
994
995
996
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
997
998
				for(c=0;c<b;c++)
					set_vmem(ptr++, option[i][c], a, 0);
999
			}
1000
1001
			else
				c=0;
1002
			while(c<width-hbrdrsize-2) {
1003
				set_vmem(ptr++, ' ', a, 0);
1004
1005
				c++;
			}
1006
1007
			if(!(mode&WIN_NOBRDR))
				set_vmem(ptr++, api->chars->list_right, hclr|(bclr<<4), 0);
1008
			i++;
1009
			j++;
1010
1011
		}
		if(!(mode&WIN_NOBRDR)) {
1012
1013
1014
1015
			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 */
1016
		}
1017
		vmem_puttext(s_left+left,s_top+top,s_left+left+width-1
1018
1019
1020
1021
1022
1023
1024
1025
			,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);
1026
			textattr(lclr|(bclr<<4));
1027
			putch(api->chars->down_arrow);	   /* put down arrow */
1028
			textattr(hclr|(bclr<<4));
1029
		}
1030

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

1038
1039
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
1040
			if(bclr==BLUE) {
1041
1042
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
1043
1044
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
1045
1046
1047
1048
				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);
1049
1050
				for(i=1;i<width*2;i+=2)
					shade[i]=DARKGRAY;
1051
1052
				puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
1053
			}
1054
		}
1055
1056
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
1057
1058
1059
1060
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
1061
		i=(*cur)+(top+tbrdrwidth-y);
1062
		j=tbrdrwidth-1;
1063
1064

		longopt=0;
1065
		while(j<height-bbrdrwidth-1) {