uifc32.c 67.7 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 2004 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
50
 *																			*
 * 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

51
#include "ciolib.h"
52
#include "keys.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
69
70
71
static win_t sav[MAX_BUFS];
static uifcapi_t* api;

/* Prototypes */
static int   uprintf(int x, int y, unsigned char attr, char *fmt,...);
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
94
95
96
97
98
99
100
101
102
103
104
105
106

/* 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;

static void reset_dynamic(void) {
	last_menu_cur=NULL;
	last_menu_bar=NULL;
	save_menu_cur=-1;
	save_menu_bar=-1;
}

/****************************************************************************/
/* Initialization function, see uifc.h for details.							*/
/* Returns 0 on success.													*/
/****************************************************************************/

107
108
void uifc_mouse_enable(void)
{
109
110
111
112
113
	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
114
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
115
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
116
117
118
119
120
121
122
123
124
	showmouse();
}

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

deuce's avatar
deuce committed
125
int kbwait(void) {
126
	int timeout=0;
127
	while(timeout++<50) {
128
		if(kbhit())
129
			return(TRUE);
130
		mswait(1);
131
132
133
134
	}
	return(FALSE);
}

135
int inkey(void)
deuce's avatar
deuce committed
136
137
138
139
{
	int c;

	c=getch();
140
141
	if(!c || c==0xff)
		c|=(getch()<<8);
deuce's avatar
deuce committed
142
143
	return(c);
}
144

145
146
147
148
149
150
151
152
153
154
int uifcini32(uifcapi_t* uifcapi)
{
	int 	i;
	struct	text_info txtinfo;

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

    api=uifcapi;

155
    /* install function handlers */
156
157
158
159
160
161
162
163
    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
164
	api->showbuf=showbuf;
165
	api->timedisplay=timedisplay;
166
	api->bottomline=bottomline;
167
	api->getstrxy=ugetstr;
168
	api->printf=uprintf;
169

170
171
172
173
	/* A esc_delay of less than 10 is stupid... silently override */
	if(api->esc_delay < 10)
		api->esc_delay=25;

174
175
176
177
178
#ifdef NCURSES_VERSION_MAJOR
	if(cio_api.mode==CIOLIB_MODE_CURSES) {
		ESCDELAY=api->esc_delay;
#endif

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
    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;
        }
    }

    clrscr();

    gettextinfo(&txtinfo);
    /* unsupported mode? */
    if(txtinfo.screenheight<MIN_LINES
deuce's avatar
deuce committed
213
/*        || txtinfo.screenheight>MAX_LINES */
deuce's avatar
deuce committed
214
        || txtinfo.screenwidth<40) {
215
216
217
        textmode(C80);  /* set mode to 80x25*/
        gettextinfo(&txtinfo);
    }
218

219
220

    api->scrn_len=txtinfo.screenheight;
deuce's avatar
deuce committed
221
222
223
    if(api->scrn_len<MIN_LINES) {
        cprintf("\7UIFC: Screen length (%u) must be %d lines or greater\r\n"
            ,api->scrn_len,MIN_LINES);
224
225
226
227
        return(-2);
    }
    api->scrn_len--; /* account for status line */

228
229
    if(txtinfo.screenwidth<40) {
        cprintf("\7UIFC: Screen width (%u) must be at least 40 characters\r\n"
230
231
232
233
234
235
236
            ,txtinfo.screenwidth);
        return(-3);
    }
	api->scrn_width=txtinfo.screenwidth;

    if(!(api->mode&UIFC_COLOR)
        && (api->mode&UIFC_MONO
237
238
239
240
241
242
243
244
            || 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
            || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60))
	{
245
246
247
248
249
        api->bclr=BLACK;
        api->hclr=WHITE;
        api->lclr=LIGHTGRAY;
        api->cclr=LIGHTGRAY;
			api->lbclr=BLACK|(LIGHTGRAY<<4);	/* lightbar color */
250
    } else {
251
252
253
254
255
        api->bclr=BLUE;
        api->hclr=YELLOW;
        api->lclr=WHITE;
        api->cclr=CYAN;
		api->lbclr=BLUE|(LIGHTGRAY<<4);	/* lightbar color */
256
    }
257

deuce's avatar
deuce committed
258
259
260
261
262
263
264
265
266
267
268
	blk_scrn_len=api->scrn_width*api->scrn_len*2;
	if((blk_scrn=(char *)MALLOC(blk_scrn_len))==NULL)  {
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
	if((tmp_buffer=(uchar *)MALLOC(blk_scrn_len))==NULL)  {
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
269
270
271
272
273
	if((tmp_buffer2=(uchar *)MALLOC(blk_scrn_len))==NULL)  {
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,blk_scrn_len);
				return(-1);
	}
deuce's avatar
deuce committed
274
    for(i=0;i<blk_scrn_len;i+=2) {
275
        blk_scrn[i]='';
276
        blk_scrn[i+1]=api->cclr|(api->bclr<<4);
277
278
279
280
281
    }

    cursor=_NOCURSOR;
    _setcursortype(cursor);

282
	if(cio_api.mouse) {
283
		api->mode|=UIFC_MOUSE;
284
285
		uifc_mouse_enable();
	}
286

287
    return(0);
288
289
}

290
291
292
293
294
295
296
297
298
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
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);
	screen=(unsigned char*)malloc(sbufsize);
	sbuffer=(unsigned char*)malloc(sbufsize);
	gettext(1,1,api->scrn_width,api->scrn_len+1,screen);
	while(1) {
		key=getch();
		if(key==0 || key==0xff)
			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)
									sbuffer[pos*2+1]=sbuffer[pos*2+1]&0x8F|0x10;
								else
									sbuffer[pos*2+1]=sbuffer[pos*2+1]&0x8F|0x60;
								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;
						copybuf=malloc((endy-starty+1)*(endx-startx+1)+1+lines*2);
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								copybuf[outpos++]=screen[(y*api->scrn_width+x)*2];
							}
							copybuf[outpos++]='\r';
							copybuf[outpos++]='\n';
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
						puttext(1,1,api->scrn_width,api->scrn_len+1,screen);
						free(copybuf);
						free(screen);
						free(sbuffer);
						return;
				}
				break;
			default:
				puttext(1,1,api->scrn_width,api->scrn_len+1,screen);
				ungetch(key);
				free(screen);
				free(sbuffer);
				return;
		}
	}
}

375
376
377
378
379
380
381
382
383
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);
384
385
386
387
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
		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);
}

405
406
407
408
void uifcbail(void)
{
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
409
	uifc_mouse_disable();
410
	clrscr();
deuce's avatar
deuce committed
411
412
413
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
414
415
416
417
418
419
420
421
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
int uscrn(char *str)
{
422
    textattr(api->bclr|(api->cclr<<4));
423
    gotoxy(1,1);
424
    clreol();
425
426
427
428
429
    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);
430
    clreol();
431
	reset_dynamic();
432
	settitle(str);
433
434
435
436
437
438
439
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
440
	gettext(x1,y1,x2,y2,tmp_buffer2);
441
	if(down)
442
		puttext(x1,y1+1,x2,y2,tmp_buffer2);
443
	else
444
		puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)*2));
445
446
447
448
449
450
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
451
static void timedisplay(BOOL force)
452
453
454
455
456
{
	static time_t savetime;
	time_t now;

	now=time(NULL);
457
	if(force || difftime(now,savetime)>=60) {
458
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
459
460
461
462
463
464
465
466
467
468
469
470
		savetime=now; 
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
static void truncsp(char *str)
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
471
	while(c && (uchar)str[c-1]<=' ') c--;
472
473
474
475
476
477
478
479
	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
480
	, char *initial_title, char **option)
481
{
482
	uchar line[256],shade[256],*ptr
483
		,search[MAX_OPLN],bline=0,*win;
484
485
486
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
487
488
489
490
491
492
493
494
495
496
	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;
497
	uint title_len;
498
	struct mouse_event mevnt;
499
	char	*title=NULL;
500
	int	a,b,c,longopt;
501
	int	optheight=0;
502

503
504
	title=strdup(initial_title==NULL?"":initial_title);

505
	uifc_mouse_disable();
506
507

	title_len=strlen(title);
508
509
510

	if(mode&WIN_FAT) {
		s_top=1;
511
		s_left=2;
512
513
		s_right=api->scrn_width-3;  /* Leave space for the shadow */
		s_bottom=api->scrn_len-1;   /* Leave one for the shadow */
514
	}
515
516
517
518
519
520
521
522
523
	if(mode&WIN_NOBRDR) {
		hbrdrsize=0;
		vbrdrsize=0;
		lbrdrwidth=0;
		rbrdrwidth=0;
		tbrdrwidth=0;
		bbrdrwidth=0;
	}

524
525
526
527
528
529
	if(mode&WIN_SAV && api->savnum>=MAX_BUFS-1)
		putch(7);
	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;
530
	if(mode&WIN_EDIT) bline|=BL_EDIT;
531
532
	if(api->bottomline != NULL)
		api->bottomline(bline);
deuce's avatar
deuce committed
533
	while(opts<MAX_OPTS)
534
		if(option[opts]==NULL || option[opts][0]==0)
535
536
			break;
		else opts++;
deuce's avatar
deuce committed
537
	if(mode&WIN_XTR && opts<MAX_OPTS)
538
		opts++;
539
	optheight=opts+vbrdrsize;
540
	height=optheight;
541
542
543
	if(mode&WIN_FIXEDHEIGHT) {
		height=api->list_height;
	}
544
545
	if(top+height>s_bottom)
		height=(s_bottom)-top;
546
547
	if(optheight>height)
		optheight=height;
548
549
	if(!width || width<title_len+hbrdrsize+2) {
		width=title_len+hbrdrsize+2;
550
551
		for(i=0;i<opts;i++) {
			truncsp(option[i]);
552
			if((j=strlen(option[i])+hbrdrsize+2+1)>width)
553
				width=j;
554
555
		}
	}
556
557
	if(width>(s_right+1)-s_left) {
		width=(s_right+1)-s_left;
558
559
560
561
562
		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;
563
			title_len=strlen(title);
564
		}
565
	}
566
	if(mode&WIN_L2R)
deuce's avatar
deuce committed
567
		left=(s_right-s_left-width+1)/2;
568
	else if(mode&WIN_RHT)
569
		left=s_right-(width+hbrdrsize+2+left);
570
	if(mode&WIN_T2B)
deuce's avatar
deuce committed
571
		top=(api->scrn_len-height+1)/2-2;
572
	else if(mode&WIN_BOT)
573
		top=s_bottom-height-top;
574
575
	if(left<0)
		left=0;
576
577
	if(top<0)
		top=0;
578
579
580

	/* Dynamic Menus */
	if(mode&WIN_DYN
581
582
583
			&& cur != NULL
			&& bar != NULL
			&& last_menu_cur==cur
584
585
586
587
			&& last_menu_bar==bar
			&& save_menu_cur==*cur
			&& save_menu_bar==*bar)
		is_redraw=1;
588
589
590
591
	if(mode&WIN_DYN && mode&WIN_REDRAW)
		is_redraw=1;
	if(mode&WIN_DYN && mode&WIN_NODRAW)
		is_redraw=0;
592
593
594
595
596
597

	if(!is_redraw) {
		if(mode&WIN_SAV && api->savdepth==api->savnum) {
			if((sav[api->savnum].buf=(char *)MALLOC((width+3)*(height+2)*2))==NULL) {
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
598
				free(title);
599
				uifc_mouse_enable();
600
				return(-1);
601
			}
602
603
604
605
606
607
			gettext(s_left+left,s_top+top,s_left+left+width+1
				,s_top+top+height,sav[api->savnum].buf);
			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;
608
			api->savdepth++;
609
610
		}
		else if(mode&WIN_SAV
611
612
613
614
			&& (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 */
615
616
617
618
619
620
			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(sav[api->savnum].buf);
			if((sav[api->savnum].buf=(char *)MALLOC((width+3)*(height+2)*2))==NULL) {
				cprintf("UIFC line %d: error allocating %u bytes."
					,__LINE__,(width+3)*(height+2)*2);
621
				free(title);
622
				uifc_mouse_enable();
623
				return(-1);
624
			}
625
626
627
628
629
630
			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;
631
		}
632
633
634
635
636
	}

	if(!is_redraw) {
		if(mode&WIN_ORG) { /* Clear around menu */
			if(top)
637
				puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn);
638
			if(s_top+height+top<=api->scrn_len)
deuce's avatar
deuce committed
639
				puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn);
640
			if(left)
deuce's avatar
deuce committed
641
				puttext(1,s_top+top,s_left+left-1,s_top+height+top
642
					,blk_scrn);
643
			if(s_left+left+width<=s_right)
deuce's avatar
deuce committed
644
				puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width
645
					,s_top+height+top,blk_scrn);
646
		}
deuce's avatar
deuce committed
647
		ptr=tmp_buffer;
648
649
		if(!(mode&WIN_NOBRDR)) {
			*(ptr++)='';
650
			*(ptr++)=api->hclr|(api->bclr<<4);
651

652
653
			if(api->mode&UIFC_MOUSE) {
				*(ptr++)='[';
654
				*(ptr++)=api->hclr|(api->bclr<<4);
655
656
				/* *(ptr++)=''; */
				*(ptr++)=0xfe;
657
				*(ptr++)=api->lclr|(api->bclr<<4);
658
				*(ptr++)=']';
659
				*(ptr++)=api->hclr|(api->bclr<<4);
660
				*(ptr++)='[';
661
				*(ptr++)=api->hclr|(api->bclr<<4);
662
				*(ptr++)='?';
663
				*(ptr++)=api->lclr|(api->bclr<<4);
664
				*(ptr++)=']';
665
				*(ptr++)=api->hclr|(api->bclr<<4);
666
667
668
669
670
671
672
673
674
675
676
677
				i=6;
				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++) {
				*(ptr++)='';
678
				*(ptr++)=api->hclr|(api->bclr<<4);
679
680
			}
			*(ptr++)='';
681
			*(ptr++)=api->hclr|(api->bclr<<4);
682
			*(ptr++)='';
683
			*(ptr++)=api->hclr|(api->bclr<<4);
684
685
686
687
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
				*(ptr++)=' ';
688
				*(ptr++)=api->hclr|(api->bclr<<4);
689
690
691
			}
			for(i=0;i<a;i++) {
				*(ptr++)=title[i];
692
				*(ptr++)=api->hclr|(api->bclr<<4);
693
694
695
			}
			for(i=0;i<width-(a+b)-2;i++) {
				*(ptr++)=' ';
696
				*(ptr++)=api->hclr|(api->bclr<<4);
697
698
			}
			*(ptr++)='';
699
			*(ptr++)=api->hclr|(api->bclr<<4);
700
			*(ptr++)='';
701
			*(ptr++)=api->hclr|(api->bclr<<4);
702
703
			for(i=0;i<width-2;i++) {
				*(ptr++)='';
704
				*(ptr++)=api->hclr|(api->bclr<<4);
705
706
			}
			*(ptr++)='';
707
			*(ptr++)=api->hclr|(api->bclr<<4);
708
709
710
711
712
713
		}

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

		if(!bar) {
714
715
716
717
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
718
			i=0;
719
720
721
722
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
723
724
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
725
			if((*cur)==opts-1)
726
727
728
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
729
730
731
732
733
			if((*bar)<0)
				(*bar)=0;
			if((*cur)<(*bar))
				(*cur)=(*bar);
			i=(*cur)-(*bar);
734
735
736
737
			if(i+(height-vbrdrsize-1)>=opts) {
				i=opts-(height-vbrdrsize);
				if(i<0)
					i=0;
738
739
				(*cur)=i+(*bar);
			}
740
741
742
		}
		if((*cur)<0)
			(*cur)=0;
743

744
745
746
		j=0;
		if(i<0) i=0;
		longopt=0;
747
		while(j<height-vbrdrsize) {
748
			if(!(mode&WIN_NOBRDR)) {
749
				*(ptr++)='';
750
				*(ptr++)=api->hclr|(api->bclr<<4);
751
752
			}
			*(ptr++)=' ';
753
			*(ptr++)=api->hclr|(api->bclr<<4);
754
			*(ptr++)='';
755
			*(ptr++)=api->lclr|(api->bclr<<4);
756
			if(i==(*cur))
757
				a=api->lbclr;
758
			else
759
760
761
762
763
764
765
766
767
768
769
				a=api->lclr|(api->bclr<<4);
			if(i<opts) {
				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; 
				}
770
			}
771
772
			else
				c=0;
773
			while(c<width-hbrdrsize-2) {
774
				*(ptr++)=' ';
775
776
777
778
				*(ptr++)=a;
				c++;
			}
			if(!(mode&WIN_NOBRDR)) {
779
				*(ptr++)='';
780
				*(ptr++)=api->hclr|(api->bclr<<4);
781
			}
782
783
784
785
			i++;
			j++; 
		}
		if(!(mode&WIN_NOBRDR)) {
786
			*(ptr++)='';
787
			*(ptr++)=api->hclr|(api->bclr<<4);
788
789
			for(i=0;i<width-2;i++) {
				*(ptr++)='';
790
				*(ptr++)=api->hclr|(api->bclr<<4); 
791
792
			}
			*(ptr++)='';
793
			*(ptr)=api->hclr|(api->bclr<<4);	/* Not incremented to shut ot BCC */
794
795
796
797
798
799
800
801
802
803
		}
		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);
804
			textattr(api->lclr|(api->bclr<<4));
805
			putch(31);	   /* put down arrow */
806
			textattr(api->hclr|(api->bclr<<4)); 
807
		}
808

809
810
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
811
			textattr(api->lclr|(api->bclr<<4));
812
			putch(30);	   /* put the up arrow */
813
			textattr(api->hclr|(api->bclr<<4)); 
814
		}
815

816
817
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
818
			if(api->bclr==BLUE) {
819
820
				gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
					,s_top+top+height-1,shade);
821
822
				for(i=1;i<height*4;i+=2)
					shade[i]=DARKGRAY;
823
824
825
826
				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);
827
828
				for(i=1;i<width*2;i+=2)
					shade[i]=DARKGRAY;
829
830
				puttext(s_left+left+2,s_top+top+height,s_left+left+width+1
					,s_top+top+height,shade);
831
			}
832
		}
833
834
835
836
837
838
	}
	else {	/* Is a redraw */
		i=(*cur)-(*bar);
		j=2;

		longopt=0;
839
		while(j<height-bbrdrwidth-1) {
deuce's avatar
deuce committed
840
			ptr=tmp_buffer;
841
			if(i==(*cur))
842
				a=api->lbclr;
843
			else
844
845
846
847
848
849
850
851
852
853
854
				a=api->lclr|(api->bclr<<4);
			if(i<opts) {
				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; 
				}
855
			}
856
857
			else
				c=0;
858
			while(c<width-hbrdrsize-2) {
859
860
861
862
863
864
				*(ptr++)=' ';
				*(ptr++)=a;
				c++; 
			}
			i++;
			j++; 
865
866
			puttext(s_left+left+lbrdrwidth+2,s_top+top+j,s_left+left+width-rbrdrwidth-1
				,s_top+top+j,tmp_buffer);
867
868
		}
		if(bar)
869
			y=top+tbrdrwidth+(*bar);
870
		else
871
			y=top+tbrdrwidth+(*cur);
872
	}
873
	free(title);
874
875
876

	last_menu_cur=cur;
	last_menu_bar=bar;
877
878
	uifc_mouse_enable();

879
880
	if(mode&WIN_IMM)
		return(-2);
881

882
883
884
885
	if(mode&WIN_ORG) {
		if(api->timedisplay != NULL)
			api->timedisplay(/* force? */TRUE);
	}
886

887
888
889
890
891
892
	while(1) {
	#if 0					/* debug */
		gotoxy(30,1);
		cprintf("y=%2d h=%2d c=%2d b=%2d s=%2d o=%2d"
			,y,height,*cur,bar ? *bar :0xff,api->savdepth,opts);
	#endif
893
		if(api->timedisplay != NULL)
894
			api->timedisplay(/* force? */FALSE);
895
		i=0;
896
897
		if(kbwait()) {
			i=inkey();
898
			if(i==CIO_KEY_MOUSE) {
899
				if((i=uifc_getmouse(&mevnt))==0) {
900
					/* Clicked in menu */
901
902
903
					if(mevnt.startx>=s_left+left+lbrdrwidth+2
							&& mevnt.startx<=s_left+left+width-rbrdrwidth-1
							&& mevnt.starty>=s_top+top+tbrdrwidth
904
							&& mevnt.starty<=(s_top+top+optheight)-bbrdrwidth-1
905
							&& mevnt.event==CIOLIB_BUTTON_1_CLICK) {
906

907
						(*cur)=(mevnt.starty)-(s_top+top+tbrdrwidth);
908
909
						if(bar)
							(*bar)=(*cur);
910
						y=top+tbrdrwidth+((mevnt.starty)-(s_top+top+tbrdrwidth));
911

912
						if(!opts)
913
914
915
							continue;

						if(mode&WIN_ACT) {
916
							uifc_mouse_disable();
917
918
919
920
921
922
923
924
							if((win=(char *)MALLOC((width+3)*(height+2)*2))==NULL) {
								cprintf("UIFC line %d: error allocating %u bytes."
									,__LINE__,(width+3)*(height+2)*2);
								return(-1);
							}
							gettext(s_left+left,s_top+top,s_left
								+left+width-1,s_top+top+height-1,win);
							for(i=1;i<(width*height*2);i+=2)
925
								win[i]=api->lclr|(api->cclr<<4);
926
							j=(((y-top)*width)*2)+7+((width-hbrdrsize-2)*2);
927
							for(i=(((y-top)*width)*2)+7;i<j;i+=2)
928
								win[i]=api->hclr|(api->cclr<<4);
929
930
931
932

							puttext(s_left+left,s_top+top,s_left
								+left+width-1,s_top+top+height-1,win);
							free(win);
933
							uifc_mouse_enable();
934
935
						}
						else if(mode&WIN_SAV) {
936
							uifc_mouse_disable();
937
938
939
							puttext(sav[api->savnum].left,sav[api->savnum].top
								,sav[api->savnum].right,sav[api->savnum].bot
								,sav[api->savnum].buf);
940
							uifc_mouse_enable();
941
942
943
							free(sav[api->savnum].buf);
							api->savdepth--;
						}
944
945
						if(mode&WIN_XTR && (*cur)==opts-1)
							return(MSK_INS|*cur);
946
947
948
						return(*cur);
					}
					/* Clicked Scroll Up */
949
950
					else if(mevnt.startx==s_left+left+lbrdrwidth
							&& mevnt.starty==s_top+top+tbrdrwidth
951
							&& mevnt.event==CIOLIB_BUTTON_1_CLICK) {
952
						i=CIO_KEY_PPAGE;
953
954
					}
					/* Clicked Scroll Down */
955
956
					else if(mevnt.startx==s_left+left+lbrdrwidth
							&& mevnt.starty==(s_top+top+height)-bbrdrwidth-1
957
							&& mevnt.event==CIOLIB_BUTTON_1_CLICK) {
958
						i=CIO_KEY_NPAGE;
959
					}
deuce's avatar
deuce committed
960
					/* Clicked Outside of Window */
961
					else if((mevnt.startx<s_left+left
deuce's avatar
deuce committed
962
963
964
							|| mevnt.startx>s_left+left+width-1
							|| mevnt.starty<s_top+top
							|| mevnt.starty>s_top+top+height-1)
965
							&& (mevnt.event==CIOLIB_BUTTON_1_CLICK
966
967
							|| mevnt.event==CIOLIB_BUTTON_3_CLICK)) {
						if(mode&WIN_UNGETMOUSE) {
deuce's avatar
deuce committed
968
							ungetmouse(&mevnt);
969
							i=CIO_KEY_MOUSE;
970
971
972
973
974
						}
						else {
							i=ESC;
						}
					}
975
976
				}
			}
977
978
979
980
981
982
983
984
985
986
987
988
989
			/* For compatibility with terminals lacking special keys */
			switch(i) {
				case '\b':
					i=ESC;
					break;
				case '+':
					i=CIO_KEY_IC;	/* insert */
					break;
				case '-':
				case DEL:
					i=CIO_KEY_DC;	/* delete */
					break;
				case CTRL_B:
990
991
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_HOME;
992
993
					break;
				case CTRL_E:
994
995
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_END;
996
997
					break;
				case CTRL_U:
998
999
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_PPAGE;
1000
1001
					break;
				case CTRL_D:
1002
1003
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_NPAGE;
1004
1005
					break;
				case CTRL_Z:
1006
1007
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_F(1);	/* help */
1008
1009
					break;
				case CTRL_C:
1010
1011
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_F(5);	/* copy */
1012
1013
					break;
				case CTRL_V:
1014
1015
					if(!(api->mode&UIFC_NOCTRL))
						i=CIO_KEY_F(6);	/* paste */
1016
1017
					break;
			}
1018
1019
1020
1021
			if(i>255) {
				s=0;
				switch(i) {
					/* ToDo extended keys */
1022
					case CIO_KEY_HOME:	/* home */
1023
1024
						if(!opts)
							break;
1025
						if(opts+vbrdrsize>optheight) {
1026
							gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
1027
							textattr(api->lclr|(api->bclr<<4));
1028
							putch(' ');    /* Delete the up arrow */
1029
							gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1);
1030
							putch(31);	   /* put the down arrow */
1031
							uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth
1032
								,api->lbclr
1033
								,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[0]);
1034
							for(i=1;i<optheight-vbrdrsize;i++)    /* re-display options */
1035
								uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth+i
1036
									,api->lclr|(api->bclr<<4)
1037
									,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[i]);
1038
1039
1040
							(*cur)=0;
							if(bar)
								(*bar)=0;
1041
							y=top+tbrdrwidth;
1042
1043
							break; 
						}
1044
1045
						gettext(s_left+left+lbrdrwidth+2,s_top+y
							,s_left+left+width-rbrdrwidth-1,s_top+y,line);
1046
						for(i=1;i<width*2;i+=2)
1047
							line[i]=api->lclr|(api->bclr<<4);
1048
1049
						puttext(s_left+left+lbrdrwidth+2,s_top+y
							,s_left+left+width-rbrdrwidth-1,s_top+y,line);
1050
1051
1052
						(*cur)=0;
						if(bar)
							(*bar)=0;