uifc32.c 76.4 KB
Newer Older
deuce's avatar
deuce committed
1
/* uifc32.c */
2
3
4

/* Curses implementation of UIFC (user interface) library based on uifc.c */

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

7
8
9
10
/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2010 Rob Swindell - http://www.synchro.net/copyright.html		*
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
49
 *																			*
 * 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
50
#include <genwrap.h>	// for alloca()
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
62
63
static size_t blk_scrn_len;
static char* blk_scrn;
static uchar* tmp_buffer;
64
static uchar* 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
165
166
167
168
static uifc_graphics_t ascii_chars = {
	.background='#',
	.help_char='?',
	.close_char='X',
	.up_arrow='^',
	.down_arrow='V',
	.button_left='[',
	.button_right=']',

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

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

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

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

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

219
220
void uifc_mouse_enable(void)
{
221
222
223
224
225
	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
226
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
227
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
228
229
230
231
232
233
234
235
236
	showmouse();
}

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

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

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

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

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

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

    api=uifcapi;
266
267
    if (api->chars == NULL) {
		switch(getfont()) {
268
			case -1:
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
			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;
		}
	}
286

287
    /* install function handlers */
288
289
290
291
292
293
294
295
    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
296
	api->showbuf=showbuf;
297
	api->timedisplay=timedisplay;
298
	api->bottomline=bottomline;
299
	api->getstrxy=ugetstr;
300
	api->printf=uprintf;
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
330

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

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

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

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

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

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
362
363
364
365
366
367
            || 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
368
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60
369
			|| txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25))
370
	{
371
372
373
374
375
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
376
    } else {
377
378
379
380
381
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
382
    }
383

deuce's avatar
deuce committed
384
	blk_scrn_len=api->scrn_width*api->scrn_len*2;
deuce's avatar
deuce committed
385
	if((blk_scrn=(char *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
386
387
388
389
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
390
	if((tmp_buffer=(uchar *)malloc(blk_scrn_len))==NULL)  {
deuce's avatar
deuce committed
391
392
393
394
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
395
	if((tmp_buffer2=(uchar *)malloc(blk_scrn_len))==NULL)  {
396
397
398
399
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
400
    for(i=0;i<blk_scrn_len;i+=2) {
401
        blk_scrn[i]=api->chars->background;
402
        blk_scrn[i+1]=api->cclr|(api->bclr<<4);
403
404
405
406
407
    }

    cursor=_NOCURSOR;
    _setcursortype(cursor);

408
	if(cio_api.mouse) {
409
		api->mode|=UIFC_MOUSE;
410
411
		uifc_mouse_enable();
	}
412

413
414
415
416
417
418
419
	/* 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;

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

	api->initialized=TRUE;
425

426
    return(0);
427
428
}

429
430
431
432
433
434
435
436
437
438
439
440
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
	unsigned char *screen;
	unsigned char *sbuffer;
	int sbufsize=0;
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;

	sbufsize=api->scrn_width*2*(api->scrn_len+1);
441
442
	screen=(unsigned char*)alloca(sbufsize);
	sbuffer=(unsigned char*)alloca(sbufsize);
443
444
445
	gettext(1,1,api->scrn_width,api->scrn_len+1,screen);
	while(1) {
		key=getch();
446
		if(key==0 || key==0xe0)
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
			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:
						memcpy(sbuffer,screen,sbufsize);
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
								if((sbuffer[pos*2+1]&0x70)!=0x10)
deuce's avatar
deuce committed
474
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x10;
475
								else
deuce's avatar
deuce committed
476
									sbuffer[pos*2+1]=(sbuffer[pos*2+1]&0x8F)|0x60;
477
478
479
480
481
482
483
484
485
								if(((sbuffer[pos*2+1]&0x70)>>4) == (sbuffer[pos*2+1]&0x0F)) {
									sbuffer[pos*2+1]|=0x08;
								}
							}
						}
						puttext(1,1,api->scrn_width,api->scrn_len+1,sbuffer);
						break;
					case CIOLIB_BUTTON_1_DRAG_END:
						lines=abs(mevent.endy-mevent.starty)+1;
486
						copybuf=alloca((endy-starty+1)*(endx-startx+1)+1+lines*2);
487
488
489
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
490
								copybuf[outpos++]=screen[(y*api->scrn_width+x)*2]?screen[(y*api->scrn_width+x)*2]:' ';
491
							}
492
493
494
							#ifdef _WIN32
								copybuf[outpos++]='\r';
							#endif
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
							copybuf[outpos++]='\n';
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
						puttext(1,1,api->scrn_width,api->scrn_len+1,screen);
						return;
				}
				break;
			default:
				puttext(1,1,api->scrn_width,api->scrn_len+1,screen);
				ungetch(key);
				return;
		}
	}
}

511
512
513
514
515
516
517
518
519
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);
520
521
522
523
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
		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);
}

541
542
void uifcbail(void)
{
deuce's avatar
deuce committed
543
544
	int i;

545
546
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
547
	uifc_mouse_disable();
548
	suspendciolib();
deuce's avatar
deuce committed
549
550
551
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
552
	api->initialized=FALSE;
deuce's avatar
deuce committed
553
554
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
555
556
557
558
559
560
561
562
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
563
    textattr(api->bclr|(api->cclr<<4));
564
    gotoxy(1,1);
565
    clreol();
566
567
568
569
570
    gotoxy(3,1);
	cputs(str);
    if(!puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn))
        return(-1);
    gotoxy(1,api->scrn_len+1);
571
    clreol();
572
	reset_dynamic();
573
	setname(str);
574
575
576
577
578
579
580
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
581
	gettext(x1,y1,x2,y2,tmp_buffer2);
582
	if(down)
583
		puttext(x1,y1+1,x2,y2,tmp_buffer2);
584
	else
585
		puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)*2));
586
587
588
589
590
591
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
592
static void timedisplay(BOOL force)
593
594
595
596
597
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
598
	if(force || difftime(now,savetime)>=60) {
599
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
600
601
602
603
604
605
606
		savetime=now; 
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
607
static void truncspctrl(char *str)
608
609
610
611
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
612
	while(c && (uchar)str[c-1]<=' ') c--;
613
614
615
616
617
618
619
620
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

/****************************************************************************/
/* 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
621
	, char *initial_title, char **option)
622
{
623
	uchar line[MAX_COLS*2],shade[MAX_LINES*4],*ptr
624
625
		,bline=0,*win;
    char search[MAX_OPLN];
626
627
628
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
629
630
631
632
633
634
635
636
637
638
	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;
639
	int title_len;
640
	struct mouse_event mevnt;
641
	char	*title=NULL;
642
	int	a,b,c,longopt;
643
	int	optheight=0;
644
	int gotkey;
645
646
647
648
649
650
651
652
653
654
655
656
657
658
	uchar	hclr,lclr,bclr,cclr,lbclr;

	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;
	}
659
660
	title=strdup(initial_title==NULL?"":initial_title);

661
662
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
663
664

	title_len=strlen(title);
665
666
667

	if(mode&WIN_FAT) {
		s_top=1;
668
		s_left=2;
669
670
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
671
	}
672
673
674
675
676
677
678
679
680
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}

681
682
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
683
	if(api->helpbuf!=NULL || api->helpixbfile[0]!=0) bline|=BL_HELP;
684
685
686
687
	if(mode&WIN_INS) bline|=BL_INS;
	if(mode&WIN_DEL) bline|=BL_DEL;
	if(mode&WIN_GET) bline|=BL_GET;
	if(mode&WIN_PUT) bline|=BL_PUT;
688
	if(mode&WIN_EDIT) bline|=BL_EDIT;
689
690
	if(api->bottomline != NULL)
		api->bottomline(bline);
691
	while(option!=NULL && opts<MAX_OPTS)
692
		if(option[opts]==NULL || option[opts][0]==0)
693
694
			break;
		else opts++;
deuce's avatar
deuce committed
695
	if(mode&WIN_XTR && opts<MAX_OPTS)
696
		opts++;
697
	optheight=opts+vbrdrsize;
698
	height=optheight;
699
700
701
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
702
703
	if(top+height>s_bottom)
		height=(s_bottom)-top;
704
705
	if(optheight>height)
		optheight=height;
706
707
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
708
		for(i=0;i<opts;i++) {
709
			if(option[i]!=NULL) {
710
				truncspctrl(option[i]);
711
712
713
				if((j=strlen(option[i])+hbrdrsize+2+1)>width)
					width=j;
			}
714
715
		}
	}
716
717
718
719
720
721
722
	/* Determine minimum widths here to accomodate mouse "icons" in border */
	if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) {
		if(bline&BL_HELP && width<8)
			width=8;
		else if(width<5)
			width=5;
	}
723
724
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
725
726
727
728
729
		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;
730
			title_len=strlen(title);
731
		}
732
	}
733
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
734
		left=(s_right-s_left-width+1)/2;
735
	else if(mode&WIN_RHT)
736
		left=s_right-(width+hbrdrsize+2+left);
737
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
738
		top=(api->scrn_len-height+1)/2-2;
739
	else if(mode&WIN_BOT)
740
		top=s_bottom-height-top;
741
742
	if(left<0)
		left=0;
743
744
	if(top<0)
		top=0;
745
746

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

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

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

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

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

859
			if(api->mode&UIFC_MOUSE) {
860
				*(ptr++)=api->chars->button_left;
861
				*(ptr++)=hclr|(bclr<<4);
862
				/* *(ptr++)=''; */
863
				*(ptr++)=api->chars->close_char;
864
				*(ptr++)=lclr|(bclr<<4);
865
				*(ptr++)=api->chars->button_right;
866
				*(ptr++)=hclr|(bclr<<4);
867
868
				i=3;
				if(bline&BL_HELP) {
869
					*(ptr++)=api->chars->button_left;
870
					*(ptr++)=hclr|(bclr<<4);
871
					*(ptr++)=api->chars->help_char;
872
					*(ptr++)=lclr|(bclr<<4);
873
					*(ptr++)=api->chars->button_right;
874
					*(ptr++)=hclr|(bclr<<4);
875
876
					i+=3;
				}
877
878
879
880
881
882
883
884
885
886
				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;

			for(;i<width-2;i++) {
887
				*(ptr++)=api->chars->list_top;
888
				*(ptr++)=hclr|(bclr<<4);
889
			}
890
			*(ptr++)=api->chars->list_top_right;
891
			*(ptr++)=hclr|(bclr<<4);
892
			*(ptr++)=api->chars->list_left;
893
			*(ptr++)=hclr|(bclr<<4);
894
895
896
897
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
898
				*(ptr++)=hclr|(bclr<<4);
899
900
901
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
902
				*(ptr++)=hclr|(bclr<<4);
903
904
905
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
906
				*(ptr++)=hclr|(bclr<<4);
907
			}
908
			*(ptr++)=api->chars->list_right;
909
			*(ptr++)=hclr|(bclr<<4);
910
			*(ptr++)=api->chars->list_separator_left;
911
			*(ptr++)=hclr|(bclr<<4);
912
			for(i=0;i<width-2;i++) {
913
				*(ptr++)=api->chars->list_horizontal_separator;
914
				*(ptr++)=hclr|(bclr<<4);
915
			}
916
			*(ptr++)=api->chars->list_separator_right;
917
			*(ptr++)=hclr|(bclr<<4);
918
919
920
921
922
923
		}

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

		if(!bar) {
924
925
926
927
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
928
			i=0;
929
930
931
932
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
933
934
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
935
			if((*cur)==opts-1)
936
937
938
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
939
940
941
942
943
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
944
945
946
947
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
948
949
				(*cur)=i+(*bar);
			}
950
951
952
		}
		if((*cur)<0)
			(*cur)=0;
953

954
955
956
		j=0;
		if(i<0) i=0;
		longopt=0;
957
		while(j<height-vbrdrsize) {
958
			if(!(mode&WIN_NOBRDR)) {
959
				*(ptr++)=api->chars->list_left;
960
				*(ptr++)=hclr|(bclr<<4);
961
962
			}
			*(ptr++)=' ';
963
			*(ptr++)=hclr|(bclr<<4);
964
			*(ptr++)=api->chars->list_scrollbar_separator;
965
			*(ptr++)=lclr|(bclr<<4);
966
			if(i==(*cur))
967
				a=lbclr;
968
			else
969
				a=lclr|(bclr<<4);
970
			if(i<opts && option[i]!=NULL) {
971
972
973
974
975
976
977
978
979
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
				for(c=0;c<b;c++) {
					*(ptr++)=option[i][c];
					*(ptr++)=a; 
				}
980
			}
981
982
			else
				c=0;
983
			while(c<width-hbrdrsize-2) {
984
				*(ptr++)=' ';
985
986
987
988
				*(ptr++)=a;
				c++;
			}
			if(!(mode&WIN_NOBRDR)) {
989
				*(ptr++)=api->chars->list_right;
990
				*(ptr++)=hclr|(bclr<<4);
991
			}
992
993
994
995
			i++;
			j++; 
		}
		if(!(mode&WIN_NOBRDR)) {
996
			*(ptr++)=api->chars->list_bottom_left;
997
			*(ptr++)=hclr|(bclr<<4);
998
			for(i=0;i<width-2;i++) {
999
				*(ptr++)=api->chars->list_bottom;
1000
				*(ptr++)=hclr|(bclr<<4); 
1001
			}
1002
			*(ptr++)=api->chars->list_bottom_right;
1003
			*(ptr)=hclr|(bclr<<4);	/* Not incremented to shut ot BCC */
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
		}
		puttext(s_left+left,s_top+top,s_left+left+width-1
			,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);
1014
			textattr(lclr|(bclr<<4));
1015
			putch(api->chars->down_arrow);	   /* put down arrow */
1016
			textattr(hclr|(bclr<<4)); 
1017
		}
1018

1019
1020
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
1021
			textattr(lclr|(bclr<<4));
1022
			putch(api->chars->up_arrow);	   /* put the up arrow */
1023
			textattr(hclr|(bclr<<4)); 
1024
		}
1025

1026
1027
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
1028
			if(bclr==BLUE) {
1029
1030
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
1031
1032
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
1033
1034
1035
1036
				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);
1037
1038
				for(i=1;i<width*2;i+=2)
					shade[i]=DARKGRAY;
1039
1040
				puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
1041
			}
1042
		}
1043
1044
	}
	else {	/* Is a redraw */
deuce's avatar
deuce committed
1045
1046
1047
1048
		if(bar)
			y=top+tbrdrwidth+(*bar);
		else
			y=top+tbrdrwidth+(*cur);
1049
		i=(*cur)+(top+tbrdrwidth-y);
1050
		j=tbrdrwidth-1;
1051
1052

		longopt=0;
1053
		while(j<height-bbrdrwidth-1) {
deuce's avatar
deuce committed
1054
			ptr=tmp_buffer;
1055
			if(i==(*cur))