uifc32.c 80.4 KB
Newer Older
1
2
/* Curses implementation of UIFC (user interface) library based on uifc.c */

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

5
6
7
8
/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10
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
 *																			*
 * 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
48
#include <genwrap.h>	// for alloca()
49

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

#define BLINK	128

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

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

/* 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;
92
static int save_menu_opts=-1;
93

94
95
char* uifcYesNoOpts[]={"Yes","No",NULL};

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

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

158
159
160
161
162
static uifc_graphics_t ascii_chars = {
	.background='#',
	.help_char='?',
	.close_char='X',
	.up_arrow='^',
163
	.down_arrow='v',
164
165
166
	.button_left='[',
	.button_right=']',

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

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

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

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

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

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

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

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

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

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

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

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

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

285
    /* install function handlers */
286
287
288
289
290
291
292
293
    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
294
	api->showbuf=showbuf;
295
	api->timedisplay=timedisplay;
296
	api->bottomline=bottomline;
297
	api->getstrxy=ugetstr;
298
	api->printf=uprintf;
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328

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

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

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

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

352
    if(txtinfo.screenwidth<40) {
353
354
		uifcbail();
        printf("\r\nUIFC: 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 && !(api->mode&UIFC_NOMOUSE)) {
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,*win;
624
    char search[MAX_OPLN];
625
626
627
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
628
629
630
631
632
633
634
635
636
637
	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;
638
	int title_len;
639
	int tmpcur=0;
640
	struct mouse_event mevnt;
641
	char	*title=NULL;
642
	int	a,b,c,longopt;
643
	int	optheight=0;
644
	int gotkey;
645
646
	uchar	hclr,lclr,bclr,cclr,lbclr;

647
	if(cur==NULL) cur=&tmpcur;
648
	api->exit_flags = 0;
649
650
651
652
653
654
655
656
657
658
659
660
	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;
	}
661
662
	title=strdup(initial_title==NULL?"":initial_title);

663
664
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
665
666

	title_len=strlen(title);
667
668
669

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

691
	/* Sanity-check the savnum */
692
693
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
694

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

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

	/* Dynamic Menus */
755
	if(mode&WIN_DYN
756
757
758
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
759
760
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
761
			&& save_menu_bar==*bar
deuce's avatar
deuce committed
762
			&& save_menu_opts==opts) {
deuce's avatar
deuce committed
763
		is_redraw=1;
764
765
	}

766
767
768
769
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
770

deuce's avatar
deuce committed
771
772
773
	if(mode&WIN_ORG) {		/* Clear all save buffers on WIN_ORG */
		for(i=0; i< MAX_BUFS; i++)
			FREE_AND_NULL(sav[i].buf);
774
		api->savnum=0;
deuce's avatar
deuce committed
775
776
	}

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

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
852
				puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
853
			if((unsigned)(s_top+height+top)<=api->scrn_len)
deuce's avatar
deuce committed
854
				puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
855
			if(left)
deuce's avatar
deuce committed
856
				puttext(1,s_top+top,s_left+left-1,s_top+height+top
857
					,blk_scrn);
858
			if(s_left+left+width<=s_right)
deuce's avatar
deuce committed
859
				puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
860
					,s_top+height+top,blk_scrn);
861
		}
deuce's avatar
deuce committed
862
		ptr=tmp_buffer;
863
		if(!(mode&WIN_NOBRDR)) {
864
			*(ptr++)=api->chars->list_top_left;
865
			*(ptr++)=hclr|(bclr<<4);
866

867
			if(api->mode&UIFC_MOUSE) {
868
				*(ptr++)=api->chars->button_left;
869
				*(ptr++)=hclr|(bclr<<4);
870
				/* *(ptr++)=''; */
871
				*(ptr++)=api->chars->close_char;
872
				*(ptr++)=lclr|(bclr<<4);
873
				*(ptr++)=api->chars->button_right;
874
				*(ptr++)=hclr|(bclr<<4);
875
				i=3;
876
				if(api->help_available) {
877
					*(ptr++)=api->chars->button_left;
878
					*(ptr++)=hclr|(bclr<<4);
879
					*(ptr++)=api->chars->help_char;
880
					*(ptr++)=lclr|(bclr<<4);
881
					*(ptr++)=api->chars->button_right;
882
					*(ptr++)=hclr|(bclr<<4);
883
884
					i+=3;
				}
885
886
887
888
889
890
891
892
893
894
				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++) {
895
				*(ptr++)=api->chars->list_top;
896
				*(ptr++)=hclr|(bclr<<4);
897
			}
898
			*(ptr++)=api->chars->list_top_right;
899
			*(ptr++)=hclr|(bclr<<4);
900
			*(ptr++)=api->chars->list_left;
901
			*(ptr++)=hclr|(bclr<<4);
902
903
904
905
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
906
				*(ptr++)=hclr|(bclr<<4);
907
908
909
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
910
				*(ptr++)=hclr|(bclr<<4);
911
912
913
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
914
				*(ptr++)=hclr|(bclr<<4);
915
			}
916
			*(ptr++)=api->chars->list_right;
917
			*(ptr++)=hclr|(bclr<<4);
918
			*(ptr++)=api->chars->list_separator_left;
919
			*(ptr++)=hclr|(bclr<<4);
920
			for(i=0;i<width-2;i++) {
921
				*(ptr++)=api->chars->list_horizontal_separator;
922
				*(ptr++)=hclr|(bclr<<4);
923
			}
924
			*(ptr++)=api->chars->list_separator_right;
925
			*(ptr++)=hclr|(bclr<<4);
926
927
928
929
930
931
		}

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

		if(!bar) {
932
933
934
935
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
936
			i=0;
937
938
939
940
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
941
942
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
943
			if((*cur)==opts-1)
944
945
946
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
947
948
949
950
951
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
952
953
954
955
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
956
957
				(*cur)=i+(*bar);
			}
958
959
960
		}
		if((*cur)<0)
			(*cur)=0;
961

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

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

1034
1035
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
1036
			if(bclr==BLUE) {
1037
1038
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
1039
1040
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
1041
1042
1043
1044
				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);
deuce's avatar