uifc32.c 85.6 KB
Newer Older
1
/* Curses implementation of UIFC (user interface) library based on uifc.c */
2
// vi: tabstop=4
3

rswindell's avatar
rswindell committed
4
5
/* $Id$ */

6
7
8
9
/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#ifdef __unix__
	#include <stdio.h>
	#include <unistd.h>
	#ifdef __QNX__
		#include <strings.h>
	#endif
    #define mswait(x) delay(x)
#elif defined(_WIN32)
	#include <share.h>
	#include <windows.h>
	#define mswait(x) Sleep(x)
#endif
49
#include <genwrap.h>	// for alloca()
50
#include <datewrap.h>	// localtime_r()
rswindell's avatar
rswindell committed
51
#include "xpprintf.h"
52

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

#define BLINK	128

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

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

/* 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);
rswindell's avatar
rswindell committed
84
85
86
87
static int  umsg(char *str);
static int  umsgf(char *fmt, ...);
static BOOL confirm(char *fmt, ...);
static BOOL deny(char *fmt, ...);
88
89
90
91
92
93
94
95
96
97
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;
98
static int save_menu_opts=-1;
99

100
101
char* uifcYesNoOpts[]={"Yes","No",NULL};

102
103
104
105
106
static void reset_dynamic(void) {
	last_menu_cur=NULL;
	last_menu_bar=NULL;
	save_menu_cur=-1;
	save_menu_bar=-1;
107
	save_menu_opts=-1;
108
109
}

110
static uifc_graphics_t cp437_chars = {
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
	.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,
};

164
165
166
167
168
/****************************************************************************/
/* Initialization function, see uifc.h for details.							*/
/* Returns 0 on success.													*/
/****************************************************************************/

169
170
void uifc_mouse_enable(void)
{
171
172
173
174
175
	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
176
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
177
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
178
179
180
181
182
183
184
185
186
	showmouse();
}

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

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

197
int inkey(void)
deuce's avatar
deuce committed
198
199
200
201
{
	int c;

	c=getch();
202
	if(!c || c==0xe0)
203
		c|=(getch()<<8);
deuce's avatar
deuce committed
204
205
	return(c);
}
206

deuce's avatar
deuce committed
207
int UIFCCALL uifcini32(uifcapi_t* uifcapi)
208
{
209
	unsigned	i;
210
211
212
213
214
215
	struct	text_info txtinfo;

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

    api=uifcapi;
216
    if (api->chars == NULL)
rswindell's avatar
rswindell committed
217
218
219
220
		api->chars = &cp437_chars;

	if (api->yesNoOpts == NULL)
		api->yesNoOpts = uifcYesNoOpts;
221

222
    /* install function handlers */
223
224
225
    api->bail=uifcbail;
    api->scrn=uscrn;
    api->msg=umsg;
rswindell's avatar
rswindell committed
226
227
228
	api->msgf=umsgf;
	api->confirm=confirm;
	api->deny=deny;
229
230
231
232
233
    api->pop=upop;
    api->list=ulist;
    api->input=uinput;
    api->sethelp=sethelp;
    api->showhelp=help;
deuce's avatar
deuce committed
234
	api->showbuf=showbuf;
235
	api->timedisplay=timedisplay;
236
	api->bottomline=bottomline;
237
	api->getstrxy=ugetstr;
238
	api->printf=uprintf;
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

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

269
#if 0
270
    clrscr();
271
#endif
272
273
274
275

    gettextinfo(&txtinfo);
    /* unsupported mode? */
    if(txtinfo.screenheight<MIN_LINES
deuce's avatar
deuce committed
276
/*        || txtinfo.screenheight>MAX_LINES */
deuce's avatar
deuce committed
277
        || txtinfo.screenwidth<40) {
278
279
280
        textmode(C80);  /* set mode to 80x25*/
        gettextinfo(&txtinfo);
    }
281
	window(1,1,txtinfo.screenwidth,txtinfo.screenheight);
282
283

    api->scrn_len=txtinfo.screenheight;
deuce's avatar
deuce committed
284
    if(api->scrn_len<MIN_LINES) {
285
286
		uifcbail();
		printf("\r\nUIFC: Screen length (%u) must be %d lines or greater\r\n"
deuce's avatar
deuce committed
287
            ,api->scrn_len,MIN_LINES);
288
289
290
291
        return(-2);
    }
    api->scrn_len--; /* account for status line */

292
    if(txtinfo.screenwidth<40) {
293
294
		uifcbail();
        printf("\r\nUIFC: Screen width (%u) must be at least 40 characters\r\n"
295
296
297
298
299
300
301
            ,txtinfo.screenwidth);
        return(-3);
    }
	api->scrn_width=txtinfo.screenwidth;

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
302
303
304
305
306
307
            || 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
308
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
309
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
310
	{
311
312
313
314
315
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
316
    } else {
317
318
319
320
321
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
322
    }
323

324
325
	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
326
				cprintf("UIFC line %d: error allocating %u bytes."
327
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
328
329
				return(-1);
	}
330
	if((tmp_buffer=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
331
				cprintf("UIFC line %d: error allocating %u bytes."
332
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
333
334
				return(-1);
	}
335
	if((tmp_buffer2=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
336
				cprintf("UIFC line %d: error allocating %u bytes."
337
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
338
339
				return(-1);
	}
340
341
342
343
344
345
    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);
	}
346
347
348
349

    cursor=_NOCURSOR;
    _setcursortype(cursor);

350
	if(cio_api.mouse && !(api->mode&UIFC_NOMOUSE)) {
351
		api->mode|=UIFC_MOUSE;
352
353
		uifc_mouse_enable();
	}
354

355
356
357
358
359
360
361
	/* 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;

362
363
	for(i=0; i<MAX_BUFS; i++)
		sav[i].buf=NULL;
deuce's avatar
deuce committed
364
365
366
	api->savnum=0;

	api->initialized=TRUE;
367

368
    return(0);
369
370
}

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

								if (sbuffer->vmem[pos].bg != bg1)
									sbuffer->vmem[pos].bg = bg1;
425
								else
426
427
428
429
430
431
432
433
434
									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;
435
436
							}
						}
437
						restorescreen(sbuffer);
438
439
440
						break;
					case CIOLIB_BUTTON_1_DRAG_END:
						lines=abs(mevent.endy-mevent.starty)+1;
deuce's avatar
deuce committed
441
						copybuf=malloc(((endy-starty+1)*(endx-startx+1)+1+lines*2)*4);
442
443
444
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
deuce's avatar
deuce committed
445
446
447
448
449
450
451
452
453
454
								size_t outlen;
								uint8_t *utf8str;
								char ch;

								ch = screen->vmem[(y*api->scrn_width+x)].ch ? screen->vmem[(y*api->scrn_width+x)].ch : ' ';
								utf8str = cp_to_utf8(conio_fontdata[screen->vmem[(y*api->scrn_width+x)].font].cp, &ch, 1, &outlen);
								if (utf8str == NULL)
									continue;
								memcpy(copybuf + outpos, utf8str, outlen);
								outpos += outlen;
455
							}
456
457
458
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
459
460
461
462
							copybuf[outpos++]='\n';
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
deuce's avatar
deuce committed
463
						free(copybuf);
464
465
466
						restorescreen(screen);
						freescreen(screen);
						freescreen(sbuffer);
467
468
469
470
						return;
				}
				break;
			default:
471
472
473
				restorescreen(screen);
				freescreen(screen);
				freescreen(sbuffer);
474
475
476
477
478
479
				ungetch(key);
				return;
		}
	}
}

480
481
482
483
484
485
486
487
488
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);
489
490
491
492
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
		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);
}

510
511
void uifcbail(void)
{
deuce's avatar
deuce committed
512
513
	int i;

514
515
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
516
	uifc_mouse_disable();
517
	suspendciolib();
deuce's avatar
deuce committed
518
519
520
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
521
	api->initialized=FALSE;
deuce's avatar
deuce committed
522
523
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
524
525
526
527
528
529
530
531
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
532
    textattr(api->bclr|(api->cclr<<4));
533
    gotoxy(1,1);
534
    clreol();
535
536
    gotoxy(3,1);
	cputs(str);
537
    if(!vmem_puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn))
538
539
        return(-1);
    gotoxy(1,api->scrn_len+1);
540
    clreol();
541
	reset_dynamic();
542
	setname(str);
543
544
545
546
547
548
549
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
550
	vmem_gettext(x1,y1,x2,y2,tmp_buffer2);
551
	if(down)
552
		vmem_puttext(x1,y1+1,x2,y2,tmp_buffer2);
553
	else
554
		vmem_puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)));
555
556
557
558
559
560
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
561
static void timedisplay(BOOL force)
562
563
{
	static time_t savetime;
564
	static int savemin;
565
	time_t now;
566
	struct tm gm;
567
	int old_hold;
568
569

	now=time(NULL);
570
571
	localtime_r(&now, &gm);
	if(force || savemin != gm.tm_min || difftime(now,savetime)>=60) {
572
573
		old_hold=hold_update;
		hold_update=FALSE;
574
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
575
		hold_update=old_hold;
576
		savetime=now;
577
		savemin = gm.tm_min;
578
579
580
581
582
583
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
584
static void truncspctrl(char *str)
585
586
587
588
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
589
	while(c && (uchar)str[c-1]<=' ') c--;
590
591
592
593
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

594
static void
595
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)
596
597
598
599
600
601
602
603
{
	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));
604
605
	j=(((y-btop)*width))+3+((width-hbrdrsize-2));
	for(i=(((y-btop)*width))+3;i<j;i++)
606
607
608
609
610
		set_vmem_attr(&buf[i], hclr|(cclr<<4));

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

611
612
613
614
/****************************************************************************/
/* 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
615
	, char *initial_title, char **option)
616
{
deuce's avatar
deuce committed
617
	struct vmem_cell *ptr, *win, shade[MAX_LINES*2], line[MAX_COLS];
rswindell's avatar
rswindell committed
618
	static char search[MAX_OPLN] = "";
619
620
621
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
622
623
624
625
626
627
628
629
630
631
	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;
632
	int title_len;
633
	int tmpcur=0;
634
	struct mouse_event mevnt;
635
	char	*title=NULL;
636
	int	a,b,c,longopt;
637
	int	optheight=0;
638
	int gotkey;
639
640
	uchar	hclr,lclr,bclr,cclr,lbclr;

641
	if(cur==NULL) cur=&tmpcur;
642
	api->exit_flags = 0;
643
644
645
646
647
648
649
650
651
652
653
654
	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;
	}
655
656
	title=strdup(initial_title==NULL?"":initial_title);

657
658
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
659
660

	title_len=strlen(title);
661
662
663

	if(mode&WIN_FAT) {
		s_top=1;
664
		s_left=2;
665
666
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
667
	}
668
669
670
671
672
673
674
675
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}
676
677
678
679
680
681
682
683
	/* 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++;
684

685
	/* Sanity-check the savnum */
686
687
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
688

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

691
	/* Create the status bar/bottom-line */
692
	int bline = mode;
693
694
	if (api->bottomline != NULL) {
		if ((mode&(WIN_XTR | WIN_PASTEXTR)) == WIN_XTR && (*cur) == opts - 1)
695
			api->bottomline(bline & ~WIN_PASTE);
696
697
698
		else
			api->bottomline(bline);
	}
699
	optheight=opts+vbrdrsize;
700
	height=optheight;
701
702
703
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
704
705
	if(top+height>s_bottom)
		height=(s_bottom)-top;
706
707
	if(optheight>height)
		optheight=height;
708
709
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
710
		for(i=0;i<opts;i++) {
711
			if(option[i]!=NULL) {
712
				truncspctrl(option[i]);
713
714
715
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
716
717
		}
	}
718
	/* Determine minimum widths here to accommodate mouse "icons" in border */
719
	if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) {
720
		if(api->help_available && width<8)
721
722
723
724
			width=8;
		else if(width<5)
			width=5;
	}
725
726
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
727
728
729
730
731
		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;
732
			title_len=strlen(title);
733
		}
734
	}
735
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
736
		left=(s_right-s_left-width+1)/2;
737
	else if(mode&WIN_RHT)
738
		left=s_right-(width+hbrdrsize+2+left);
739
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
740
		top=(api->scrn_len-height+1)/2-2;
741
	else if(mode&WIN_BOT)
742
		top=s_bottom-height-top;
743
744
	if(left<0)
		left=0;
745
746
	if(top<0)
		top=0;
747
748

	/* Dynamic Menus */
749
	if(mode&WIN_DYN
750
751
752
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
753
754
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
755
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
756
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
757
		is_redraw=1;
758
759
	}

760
761
762
763
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
764

deuce's avatar
deuce committed
765
766
767
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
768
		api->savnum=0;
deuce's avatar
deuce committed
769
770
	}

771
	if(mode&WIN_SAV) {
772
773
774
775
776
777
		/* 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--) {
778
						/* Restore old screens */
deuce's avatar
deuce committed
779
780
						vmem_puttext(sav[j].left,sav[j].top,sav[j].right,sav[j].bot
							,(void *)sav[j].buf);	/* put original window back */
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
						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
796
797
					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 */
798
					FREE_AND_NULL(sav[api->savnum].buf);
deuce's avatar
deuce committed
799
					if ((sav[api->savnum].buf = malloc((width + 3) * (height + 2) * sizeof(struct vmem_cell)))==NULL) {
800
						cprintf("UIFC line %d: error allocating %u bytes."
801
							,__LINE__,(width+3)*(height+2)*sizeof(struct vmem_cell));
802
						free(title);
803
804
						if(!(api->mode&UIFC_NHM))
							uifc_mouse_enable();
805
806
						return(-1);
					}
deuce's avatar
deuce committed
807
808
					vmem_gettext(s_left+left,s_top+top,s_left+left+width+1
						,s_top+top+height,(void *)sav[api->savnum].buf);	  /* save again */
809
810
811
812
813
814
815
816
817
818
819
820
					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++;
821
822
			}
		}
823
		else {
deuce's avatar
deuce committed
824
			if((sav[api->savnum].buf=malloc((width+3)*(height+2)*sizeof(struct vmem_cell)))==NULL) {
825
				cprintf("UIFC line %d: error allocating %u bytes."
826
					,__LINE__,(width+3)*(height+2)*sizeof(struct vmem_cell));
827
				free(title);
828
829
				if(!(api->mode&UIFC_NHM))
					uifc_mouse_enable();
830
				return(-1);
831
			}
deuce's avatar
deuce committed
832
833
			vmem_gettext(s_left+left,s_top+top,s_left+left+width+1
				,s_top+top+height,(void *)sav[api->savnum].buf);
834
835
836
837
			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;
838
839
			sav[api->savnum].cur=cur;
			sav[api->savnum].bar=bar;
840
		}
841
842
843
844
845
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
846
				vmem_puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
847
			if((unsigned)(s_top+height+top)<=api->scrn_len)
848
				vmem_puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
849
			if(left)
850
				vmem_puttext(1,s_top+top,s_left+left-1,s_top+height+top
851
					,blk_scrn);
852
			if(s_left+left+width<=s_right)
853
				vmem_puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
854
					,s_top+height+top,blk_scrn);
855
		}
deuce's avatar
deuce committed
856
		ptr=tmp_buffer;
857
		if(!(mode&WIN_NOBRDR)) {
858
			set_vmem(ptr++, api->chars->list_top_left, hclr|(bclr<<4), 0);
859

860
			if(api->mode&UIFC_MOUSE) {
861
				set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0);
862
				/* *(ptr++)=''; */
863
864
				set_vmem(ptr++, api->chars->close_char, lclr|(bclr<<4), 0);
				set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0);
865
				i=3;
866
				if(api->help_available) {
867
868
869
					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);
870
871
					i+=3;
				}
872
873
874
875
876
877
878
879
880
				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;

881
882
883
884
			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);
885
886
887
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
888
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
889
			}
890
891
892
893
894
895
896
897
898
			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);
899
900
901
902
903
904
		}

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

		if(!bar) {
905
906
907
908
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
909
			i=0;
910
911
912
913
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
914
915
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
916
			if((*cur)==opts-1)
917
918
919
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
920
921
922
923
924
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
925
926
927
928
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
929
930
				(*cur)=i+(*bar);
			}
931
932
933
		}
		if((*cur)<0)
			(*cur)=0;
934

935
936
937
		j=0;
		if(i<0) i=0;
		longopt=0;
938
		while(j<height-vbrdrsize) {
939
940
941
942
			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);
943
			if(i==(*cur))
944
				a=lbclr;
945
			else
946
				a=lclr|(bclr<<4);
947
			if(i<opts && option[i]!=NULL) {
948
949
950
951
952
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
953
954
				for(c=0;c<b;c++)
					set_vmem(ptr++, option[i][c], a, 0);
955
			}
956
957
			else
				c=0;
958
			while(c<width-hbrdrsize-2) {
959
				set_vmem(ptr++, ' ', a, 0);
960
961
				c++;
			}
962
963
			if(!(mode&WIN_NOBRDR))
				set_vmem(ptr++, api->chars->list_right, hclr|(bclr<<4), 0);
964
			i++;
965
			j++;
966
967
		}
		if(!(mode&WIN_NOBRDR)) {
968
969
970
971
			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 */
972
		}
973
		vmem_puttext(s_left+left,s_top+top,s_left+left+width-1
974
975
976
977
978
979
980
981
			,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);
982
			textattr(lclr|(bclr<<4));
983
			putch(api->chars->down_arrow);	   /* put down arrow */
984
			textattr(hclr|(bclr<<4));
985
		}
986

987
988
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
989
			textattr(lclr|(bclr<<4));
990
			putch(api->chars->up_arrow);	   /* put the up arrow */
991
			textattr(hclr|(bclr<<4));
992
		}
993

994
995
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
996
			if(bclr==BLUE) {
deuce's avatar
deuce committed
997
				vmem_gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
998
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
999
1000
1001
				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
1002
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
1003
				vmem_gettext(s_left+left+2,s_top+top+height,s_left+left+width+1
1004
					,s_top+top+height,shade);
deuce's avatar
deuce committed
1005
1006
1007
				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
1008
					,s_top+top+height,shade);
1009
			}
1010
		}
1011
1012
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
1013
1014
1015
1016
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
1017
		i=(*cur)+(top+tbrdrwidth-y);
1018
		j=tbrdrwidth-1;
1019
1020

		longopt=0;
1021
		while(j<height-bbrdrwidth-1) {
deuce's avatar
deuce committed
1022
			ptr=tmp_buffer;
1023
			if(i==(*cur))
1024
				a=lbclr;
1025
			else
1026
				a=lclr|(bclr<<4);
1027
			if(i<opts && option[i]!=NULL) {
1028
1029
1030
1031