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

4
/* $Id: uifc32.c,v 1.268 2020/08/16 20:37:08 rswindell Exp $ */
rswindell's avatar
rswindell committed
5

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

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

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

#define BLINK	128

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

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

/* API routines */
static void uifcbail(void);
Deucе's avatar
Deucе committed
79
static int  uscrn(const char *str);
80
static int  ulist(int mode, int left, int top, int width, int *dflt, int *bar
Deucе's avatar
Deucе committed
81
82
	,const char *title, char **option);
static int  uinput(int imode, int left, int top, const char *prompt, char *str
83
	,int len ,int kmode);
Deucе's avatar
Deucе committed
84
static int  umsg(const char *str);
rswindell's avatar
rswindell committed
85
86
87
static int  umsgf(char *fmt, ...);
static BOOL confirm(char *fmt, ...);
static BOOL deny(char *fmt, ...);
Deucе's avatar
Deucе committed
88
static void upop(const char *str);
89
90
91
92
93
94
95
96
97
static void sethelp(int line, char* file);
static void showbuf(int mode, int left, int top, int width, int height, char *title
	, char *hbuf, int *curp, int *barp);

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

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

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

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

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

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

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

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

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

169
170
void uifc_mouse_enable(void)
{
171
172
173
174
175
	ciomouse_setevents(0);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END);
	ciomouse_addevent(CIOLIB_BUTTON_1_CLICK);
deuce's avatar
deuce committed
176
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
177
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
deuce's avatar
deuce committed
178
179
	ciomouse_addevent(CIOLIB_BUTTON_4_PRESS);
	ciomouse_addevent(CIOLIB_BUTTON_5_PRESS);
deuce's avatar
deuce committed
180
	mousepointer(CIOLIB_MOUSEPTR_BAR);
181
182
183
184
185
186
187
188
189
	showmouse();
}

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

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

200
int inkey(void)
deuce's avatar
deuce committed
201
202
203
204
{
	int c;

	c=getch();
205
	if(!c || c==0xe0)
206
		c|=(getch()<<8);
deuce's avatar
deuce committed
207
208
	return(c);
}
209

210
int uifcini32(uifcapi_t* uifcapi)
211
{
212
	unsigned	i;
213
214
215
216
217
218
	struct	text_info txtinfo;

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

    api=uifcapi;
219
    if (api->chars == NULL)
rswindell's avatar
rswindell committed
220
221
222
223
		api->chars = &cp437_chars;

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

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

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

272
#if 0
273
    clrscr();
274
#endif
275
276
277
278

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

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

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

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

327
328
	blk_scrn_len=api->scrn_width*api->scrn_len;
	if((blk_scrn=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
329
				cprintf("UIFC line %d: error allocating %u bytes."
330
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
331
332
				return(-1);
	}
333
	if((tmp_buffer=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
deuce's avatar
deuce committed
334
				cprintf("UIFC line %d: error allocating %u bytes."
335
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
deuce's avatar
deuce committed
336
337
				return(-1);
	}
338
	if((tmp_buffer2=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL)  {
339
				cprintf("UIFC line %d: error allocating %u bytes."
340
					,__LINE__,blk_scrn_len * sizeof(*blk_scrn));
341
342
				return(-1);
	}
343
344
345
346
347
348
    for(i=0;i<blk_scrn_len;i++) {
		blk_scrn[i].legacy_attr = api->cclr|(api->bclr<<4);
		blk_scrn[i].ch = api->chars->background;
		blk_scrn[i].font = 0;
		attr2palette(blk_scrn[i].legacy_attr, &blk_scrn[i].fg, &blk_scrn[i].bg);
	}
349
350
351
352

    cursor=_NOCURSOR;
    _setcursortype(cursor);

353
	if(cio_api.mouse && !(api->mode&UIFC_NOMOUSE)) {
354
		api->mode|=UIFC_MOUSE;
355
356
		uifc_mouse_enable();
	}
357

358
359
360
361
	/* A esc_delay of less than 10 is stupid... silently override */
	if(api->esc_delay < 10)
		api->esc_delay=25;

362
363
	if(cio_api.escdelay)
		*(cio_api.escdelay)=api->esc_delay;
364

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

	api->initialized=TRUE;
370

371
    return(0);
372
373
}

374
375
376
377
void docopy(void)
{
	int	key;
	struct mouse_event mevent;
378
379
380
	struct ciolib_screen *screen;
	struct ciolib_screen *sbuffer;
	int sbufsize;
381
382
383
	int x,y,startx,starty,endx,endy,lines;
	int outpos;
	char *copybuf;
384
385
386
387
388
389
390
391
392
393
	uint32_t bg0;
	uint32_t bg1;
	uint32_t bg6;
	uint32_t fg8;

	attr2palette(0x08, &fg8, &bg0);
	attr2palette(0x10, NULL, &bg1);
	attr2palette(0x60, NULL, &bg6);
	screen=ciolib_savescreen();
	sbuffer=ciolib_savescreen();
394
395
	freepixels(sbuffer->pixels);
	sbuffer->pixels = NULL;
396
	sbufsize=screen->text_info.screenwidth * screen->text_info.screenheight * sizeof(screen->vmem[0]);
397
398
	while(1) {
		key=getch();
399
		if(key==0 || key==0xe0)
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
			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:
422
						memcpy(sbuffer->vmem,screen->vmem,sbufsize);
423
424
425
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
								int pos=y*api->scrn_width+x;
426
427
428

								if (sbuffer->vmem[pos].bg != bg1)
									sbuffer->vmem[pos].bg = bg1;
429
								else
430
431
432
433
434
435
436
437
438
									sbuffer->vmem[pos].bg = bg6;
								if (sbuffer->vmem[pos].bg == sbuffer->vmem[pos].fg)
									attr2palette(sbuffer->vmem[pos].legacy_attr | 0x08, &sbuffer->vmem[pos].fg, &sbuffer->vmem[pos].bg);
								if ((sbuffer->vmem[pos].legacy_attr & 0x70) != 0x10)
									sbuffer->vmem[pos].legacy_attr = (sbuffer->vmem[pos].legacy_attr & 0x8f) | 0x10;
								else
									sbuffer->vmem[pos].legacy_attr = (sbuffer->vmem[pos].legacy_attr & 0x8f) | 0x60;
								if (((sbuffer->vmem[pos].legacy_attr & 0x70) >> 4) == (sbuffer->vmem[pos].legacy_attr & 0x08))
									sbuffer->vmem[pos].legacy_attr |= 0x08;
439
440
							}
						}
441
						restorescreen(sbuffer);
442
443
444
						break;
					case CIOLIB_BUTTON_1_DRAG_END:
						lines=abs(mevent.endy-mevent.starty)+1;
deuce's avatar
deuce committed
445
						copybuf=malloc(((endy-starty+1)*(endx-startx+1)+1+lines*2)*4);
446
447
448
						outpos=0;
						for(y=starty-1;y<endy;y++) {
							for(x=startx-1;x<endx;x++) {
deuce's avatar
deuce committed
449
450
451
452
453
454
455
456
457
458
								size_t outlen;
								uint8_t *utf8str;
								char ch;

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

484
485
486
487
488
489
490
491
492
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);
493
494
495
496
		if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) {
			docopy();
			return(0);
		}
497
498
499
500
501
502
503
504
505
506
507
508
		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));
			}
		}
deuce's avatar
deuce committed
509
510
511
512
		if (mevent->event == CIOLIB_BUTTON_4_PRESS)
			return(CIO_KEY_UP);
		if (mevent->event == CIOLIB_BUTTON_5_PRESS)
			return(CIO_KEY_DOWN);
513
514
515
516
517
		return(0);
	}
	return(-1);
}

518
519
void uifcbail(void)
{
deuce's avatar
deuce committed
520
521
	int i;

522
523
	_setcursortype(_NORMALCURSOR);
	textattr(LIGHTGRAY);
524
	uifc_mouse_disable();
525
	suspendciolib();
deuce's avatar
deuce committed
526
527
528
	FREE_AND_NULL(blk_scrn);
	FREE_AND_NULL(tmp_buffer);
	FREE_AND_NULL(tmp_buffer2);
529
	api->initialized=FALSE;
deuce's avatar
deuce committed
530
531
	for(i=0; i< MAX_BUFS; i++)
		FREE_AND_NULL(sav[i].buf);
532
533
534
535
536
537
}

/****************************************************************************/
/* Clear screen, fill with background attribute, display application title.	*/
/* Returns 0 on success.													*/
/****************************************************************************/
Deucе's avatar
Deucе committed
538
int uscrn(const char *str)
539
{
540
    textattr(api->bclr|(api->cclr<<4));
541
    gotoxy(1,1);
542
    clreol();
543
544
    gotoxy(3,1);
	cputs(str);
545
    if(!vmem_puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn))
546
547
        return(-1);
    gotoxy(1,api->scrn_len+1);
548
    clreol();
549
	reset_dynamic();
550
	setname(str);
551
552
553
554
555
556
557
    return(0);
}

/****************************************************************************/
/****************************************************************************/
static void scroll_text(int x1, int y1, int x2, int y2, int down)
{
558
	vmem_gettext(x1,y1,x2,y2,tmp_buffer2);
559
	if(down)
560
		vmem_puttext(x1,y1+1,x2,y2,tmp_buffer2);
561
	else
562
		vmem_puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1)));
563
564
565
566
567
568
}

/****************************************************************************/
/* Updates time in upper left corner of screen with current time in ASCII/  */
/* Unix format																*/
/****************************************************************************/
569
static void timedisplay(BOOL force)
570
571
{
	static time_t savetime;
572
	static int savemin;
573
	time_t now;
574
	struct tm gm;
575
	int old_hold;
576
577

	now=time(NULL);
578
579
	localtime_r(&now, &gm);
	if(force || savemin != gm.tm_min || difftime(now,savetime)>=60) {
580
581
		old_hold=hold_update;
		hold_update=FALSE;
582
		uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now));
583
		hold_update=old_hold;
584
		savetime=now;
585
		savemin = gm.tm_min;
586
587
588
589
590
591
	}
}

/****************************************************************************/
/* Truncates white-space chars off end of 'str'								*/
/****************************************************************************/
592
static void truncspctrl(char *str)
593
594
595
596
{
	uint c;

	c=strlen(str);
deuce's avatar
deuce committed
597
	while(c && (uchar)str[c-1]<=' ') c--;
598
599
600
601
	if(str[c]!=0)	/* don't write to string constants */
		str[c]=0;
}

602
static void
603
inactive_win(struct vmem_cell *buf, int left, int top, int right, int bottom, int y, int hbrdrsize, uchar cclr, uchar lclr, uchar hclr, int btop)
604
605
606
607
608
609
610
611
{
	int width = right - left + 1;
	int height = bottom - top + 1;
	int i, j;

	vmem_gettext(left, top, right, bottom, buf);
	for (i=0; i < (width * height); i++)
		set_vmem_attr(&buf[i], lclr | (cclr<<4));
612
613
	j=(((y-btop)*width))+3+((width-hbrdrsize-2));
	for(i=(((y-btop)*width))+3;i<j;i++)
614
615
616
617
618
		set_vmem_attr(&buf[i], hclr|(cclr<<4));

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

619
620
621
622
/****************************************************************************/
/* General menu function, see uifc.h for details.							*/
/****************************************************************************/
int ulist(int mode, int left, int top, int width, int *cur, int *bar
Deucе's avatar
Deucе committed
623
	, const char *initial_title, char **option)
624
{
deuce's avatar
deuce committed
625
	struct vmem_cell *ptr, *win, shade[MAX_LINES*2], line[MAX_COLS];
rswindell's avatar
rswindell committed
626
	static char search[MAX_OPLN] = "";
627
628
629
	int height,y;
	int i,j,opts=0,s=0; /* s=search index into options */
	int	is_redraw=0;
630
631
632
633
634
635
636
637
638
639
	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;
640
	int title_len;
641
	int tmpcur=0;
642
	struct mouse_event mevnt;
643
	char	*title=NULL;
644
	int	a,b,c,longopt;
645
	int	optheight=0;
646
	int gotkey;
647
648
	uchar	hclr,lclr,bclr,cclr,lbclr;

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

665
666
	if(!(api->mode&UIFC_NHM))
		uifc_mouse_disable();
667
668

	title_len=strlen(title);
669
670
671

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

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

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

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

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

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

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

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

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

868
			if(api->mode&UIFC_MOUSE) {
869
				set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0);
870
				/* *(ptr++)=''; */
871
872
				set_vmem(ptr++, api->chars->close_char, lclr|(bclr<<4), 0);
				set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0);
873
				i=3;
874
				if(api->help_available) {
875
876
877
					set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0);
					set_vmem(ptr++, api->chars->help_char, lclr|(bclr<<4), 0);
					set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0);
878
879
					i+=3;
				}
880
881
882
883
884
885
886
887
888
				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;

889
890
891
892
			for(;i<width-2;i++)
				set_vmem(ptr++, api->chars->list_top, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_top_right, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_left, hclr|(bclr<<4), 0);
893
894
895
			a=title_len;
			b=(width-a-1)/2;
			for(i=0;i<b;i++) {
896
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
897
			}
898
899
900
901
902
903
904
905
906
			for(i=0;i<a;i++)
				set_vmem(ptr++, title[i], hclr|(bclr<<4), 0);
			for(i=0;i<width-(a+b)-2;i++)
				set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_right, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_separator_left, hclr|(bclr<<4), 0);
			for(i=0;i<width-2;i++)
				set_vmem(ptr++, api->chars->list_horizontal_separator, hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_separator_right, hclr|(bclr<<4), 0);
907
908
909
910
911
912
		}

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

		if(!bar) {
913
914
915
916
			if((*cur)>height-vbrdrsize-1)
				(*cur)=height-vbrdrsize-1;
			if((*cur)>opts-1)
				(*cur)=opts-1;
917
			i=0;
918
919
920
921
		}
		else {
			if((*bar)>=opts)
				(*bar)=opts-1;
922
923
			if((*bar)>height-vbrdrsize-1)
				(*bar)=height-vbrdrsize-1;
924
			if((*cur)==opts-1)
925
926
927
				(*bar)=height-vbrdrsize-1;
			if((*bar)>opts-1)
				(*bar)=opts-1;
928
929
930
			if((*bar)<0)
				(*bar)=0;
			i=(*cur)-(*bar);
Deucе's avatar
Deucе committed
931
932
933
934
			if (i < 0) {
				*bar += i;
				i = 0;
			}
935
			if(i+(height-vbrdrsize-1)>=opts) {
Deucе's avatar
Deucе committed
936
				(*bar)=(height-vbrdrsize);
937
938
				if (*bar > *cur)
					*bar = *cur;
Deucе's avatar
Deucе committed
939
				i=(*cur)-(*bar)+1;
940
			}
941
942
943
		}
		if((*cur)<0)
			(*cur)=0;
944

945
946
947
		j=0;
		if(i<0) i=0;
		longopt=0;
948
		while(j<height-vbrdrsize) {
949
950
951
952
			if(!(mode&WIN_NOBRDR))
				set_vmem(ptr++, api->chars->list_left, hclr|(bclr<<4), 0);
			set_vmem(ptr++, ' ', hclr|(bclr<<4), 0);
			set_vmem(ptr++, api->chars->list_scrollbar_separator, lclr|(bclr<<4), 0);
953
			if(i==(*cur))
954
				a=lbclr;
955
			else
956
				a=lclr|(bclr<<4);
957
			if(i<opts && option[i]!=NULL) {
958
959
960
961
962
				b=strlen(option[i]);
				if(b>longopt)
					longopt=b;
				if(b+hbrdrsize+2>width)
					b=width-hbrdrsize-2;
963
964
				for(c=0;c<b;c++)
					set_vmem(ptr++, option[i][c], a, 0);
965
			}
966
967
			else
				c=0;
968
			while(c<width-hbrdrsize-2) {
969
				set_vmem(ptr++, ' ', a, 0);
970
971
				c++;
			}
972
973
			if(!(mode&WIN_NOBRDR))
				set_vmem(ptr++, api->chars->list_right, hclr|(bclr<<4), 0);
974
			i++;
975
			j++;
976
977
		}
		if(!(mode&WIN_NOBRDR)) {
978
979
980
981
			set_vmem(ptr++, api->chars->list_bottom_left, hclr|(bclr<<4), 0);
			for(i=0;i<width-2;i++)
				set_vmem(ptr++, api->chars->list_bottom, hclr|(bclr<<4), 0);
			set_vmem(ptr, api->chars->list_bottom_right, hclr|(bclr<<4), 0);	/* Not incremented to shut up BCC */
982
		}
983
		vmem_puttext(s_left+left,s_top+top,s_left+left+width-1
984
985
986
987
988
989
990
991
			,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);
992
			textattr(lclr|(bclr<<4));
993
			putch(api->chars->down_arrow);	   /* put down arrow */
994
			textattr(hclr|(bclr<<4));
995
		}
996

997
998
		if(bar && (*bar)!=(*cur)) {
			gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth);
999
			textattr(lclr|(bclr<<4));
1000
			putch(api->chars->up_arrow);	   /* put the up arrow */
1001
			textattr(hclr|(bclr<<4));
1002
		}
1003

1004
1005
		if(!(mode&WIN_NOBRDR)) {
			/* Shadow */
1006
			if(api->bclr==BLUE) {
deuce's avatar
deuce committed
1007
				vmem_gettext(s_left+left+width,s_top+top+1,s_left+left+width+1
1008
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
1009
1010
1011
				for(i=0;i<height*2;i++)
					set_vmem_attr(&shade[i], DARKGRAY);
				vmem_puttext(s_left+left+width,s_top+top+1,s_left+left+width+1
1012
					,s_top+top+height-1,shade);
deuce's avatar
deuce committed
1013
				vmem_gettext(s_left+left+2,s_top+top+height,s_left+left+width+1