uifc32.c 80.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()
51

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

#define BLINK	128

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
292
293
294
295
296
297
            || txtinfo.currmode==MONO || txtinfo.currmode==BW40 || txtinfo.currmode==BW80
            || txtinfo.currmode==MONO14 || txtinfo.currmode==BW40X14 || txtinfo.currmode==BW80X14
            || txtinfo.currmode==MONO21 || txtinfo.currmode==BW40X21 || txtinfo.currmode==BW80X21
            || txtinfo.currmode==MONO28 || txtinfo.currmode==BW40X28 || txtinfo.currmode==BW80X28
            || txtinfo.currmode==MONO43 || txtinfo.currmode==BW40X43 || txtinfo.currmode==BW80X43
            || txtinfo.currmode==MONO50 || txtinfo.currmode==BW40X50 || txtinfo.currmode==BW80X50
deuce's avatar
deuce committed
298
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
299
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
300
	{
301
302
303
304
305
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
306
    } else {
307
308
309
310
311
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
312
    }
313

314
315
	blk_scrn_len=api->scrn_width*api->scrn_len;
	if((blk_scrn=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
316
				cprintf("UIFC line %d: error allocating %u bytes."
317
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
318
319
				return(-1);
	}
320
	if((tmp_buffer=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
321
				cprintf("UIFC line %d: error allocating %u bytes."
322
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
323
324
				return(-1);
	}
325
	if((tmp_buffer2=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
326
				cprintf("UIFC line %d: error allocating %u bytes."
327
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
328
329
				return(-1);
	}
330
331
332
333
334
335
    for(i=0;i<blk_scrn_len;i++) {
		blk_scrn[i].legacy_attr = api->cclr|(api->bclr<<4);
		blk_scrn[i].ch = api->chars->background;
		blk_scrn[i].font = 0;
		attr2palette(blk_scrn[i].legacy_attr, &blk_scrn[i].fg, &blk_scrn[i].bg);
	}
336
337
338
339

    cursor=_NOCURSOR;
    _setcursortype(cursor);

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

345
346
347
348
349
350
351
	/* A esc_delay of less than 10 is stupid... silently override */
	if(api->esc_delay < 10)
		api->esc_delay=25;

	if(cio_api.ESCDELAY)
		*(cio_api.ESCDELAY)=api->esc_delay;

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

	api->initialized=TRUE;
357

358
    return(0);
359
360
}

361
362
363
364
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
365
366
367
	struct ciolib_screen *screen;
	struct ciolib_screen *sbuffer;
	int sbufsize;
368
369
370
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;
371
372
373
374
375
376
377
378
379
380
381
382
	uint32_t bg0;
	uint32_t bg1;
	uint32_t bg6;
	uint32_t fg8;

	attr2palette(0x08, &fg8, &bg0);
	attr2palette(0x10, NULL, &bg1);
	attr2palette(0x60, NULL, &bg6);
	screen=ciolib_savescreen();
	sbuffer=ciolib_savescreen();
	FREE_AND_NULL(sbuffer->pixels);
	sbufsize=screen->text_info.screenwidth * screen->text_info.screenheight * sizeof(screen->vmem[0]);
383
384
	while(1) {
		key=getch();
385
		if(key==0 || key==0xe0)
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
			key|=getch()<<8;
		switch(key) {
			case CIO_KEY_MOUSE:
				getmouse(&mevent);
				if(mevent.startx<mevent.endx) {
					startx=mevent.startx;
					endx=mevent.endx;
				}
				else {
					startx=mevent.endx;
					endx=mevent.startx;
				}
				if(mevent.starty<mevent.endy) {
					starty=mevent.starty;
					endy=mevent.endy;
				}
				else {
					starty=mevent.endy;
					endy=mevent.starty;
				}
				switch(mevent.event) {
					case CIOLIB_BUTTON_1_DRAG_MOVE:
408
						memcpy(sbuffer->vmem,screen->vmem,sbufsize);
409
410
411
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
412
413
414

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

461
462
463
464
465
466
467
468
469
static int uifc_getmouse(struct mouse_event *mevent)
{
	mevent->startx=0;
	mevent->starty=0;
	mevent->event=0;
	if(api->mode&UIFC_MOUSE) {
		getmouse(mevent);
		if(mevent->event==CIOLIB_BUTTON_3_CLICK)
			return(ESC);
470
471
472
473
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
		if(mevent->starty==api->buttony) {
			if(mevent->startx>=api->exitstart
					&& mevent->startx<=api->exitend
					&& mevent->event==CIOLIB_BUTTON_1_CLICK) {
				return(ESC);
			}
			if(mevent->startx>=api->helpstart
					&& mevent->startx<=api->helpend
					&& mevent->event==CIOLIB_BUTTON_1_CLICK) {
				return(CIO_KEY_F(1));
			}
		}
		return(0);
	}
	return(-1);
}

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

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

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

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

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

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

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
565
static void truncspctrl(char *str)
566
567
568
569
{
	uint c;

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

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

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

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

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

638
639
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
640
641

	title_len=strlen(title);
642
643
644

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

666
	/* Sanity-check the savnum */
667
668
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
669

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

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

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

741
742
743
744
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
745

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

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

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

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

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

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

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

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

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

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

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