/****************************************************************************

 Programma dimostrativo dei gadget proporzionali.

 Scritto da
 Nicola Salmoria
 Via Piemonte 11
 53100 Siena


 Compilazione con Aztec C 5.0a:

 cc propgad -3
 ln propgad -lc16

 ****************************************************************************/

#include "intuition/intuition.h"
#include "exec/memory.h"
#include "functions.h"

/* qualche typedef e qualche macro per risparmiare un po' di scrittura */
typedef struct Gadget GADG;
typedef struct PropInfo PINF;
typedef struct Image IMGE;

#define BARINFO(x) (((PINF *)(x)->SpecialInfo))
#define BARKNOB(x) (((IMGE *)(x)->GadgetRender))



#define AUTO_LINES	30	/* valori di 'numlines' e 'pagesize' per i gadget */
#define AUTO_SIZE	10		/* sulla destra */

#define STRING_LINES	40	/* valori di 'numlines' e 'pagesize' per il gadget */
#define STRING_SIZE	15	/* che muove l'elenco di stringhe */


/* prototipi delle funzioni. DA USARE SEMPRE */
VOID MyText(struct RastPort *,BYTE *,SHORT,SHORT);
ULONG MyGetMsg(struct MsgPort *,struct IntuiMessage *);
GADG *CreateScrollBar(struct Window *,SHORT,SHORT,SHORT,SHORT,USHORT,USHORT,USHORT,USHORT,USHORT,USHORT);
GADG *CreateDoubleScrollBar(struct Window *,SHORT,SHORT,SHORT,SHORT,USHORT,USHORT,USHORT,USHORT,USHORT,USHORT);
VOID FreeGadget(GADG *);
VOID MoveScrollBar(GADG *,struct Window *,USHORT,USHORT);
VOID MoveDoubleScrollBar(GADG *,struct Window *,USHORT,USHORT);
ULONG BarPos(ULONG,SHORT,USHORT);
#pragma regcall(BarPos(d0,d1,d2))	/* per passare gli argomenti nei registri */
ULONG SetPos(ULONG,SHORT,ULONG);
#pragma regcall(SetPos(d0,d1,d2))
ULONG SetBody(ULONG,SHORT);
#pragma regcall(SetBody(d0,d1))
VOID autoscroll(VOID);
VOID printbugbarpos(VOID);
VOID movebugbar(SHORT);
VOID printstrings(VOID);
VOID openall(VOID);
VOID cleanup(SHORT);




/* Variabili globali. Notare: SOLO LO STRETTO NECESSARIO. Le variabili globali */
/* confondono le idee. */
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
struct Window *window;
GADG *scrollbar[5];



struct TextAttr tanormal =
{
	(UBYTE *)"topaz.font",
	8,
	FS_NORMAL,
	FPF_ROMFONT,
};


/* La definizione del gadget 'AutoScroll'. Sarebbe una buona cosa allocare */
/* dinamicamente anche questa. */

#define CONTINUEWIDTH (12*8)

SHORT continuebordxy[] = {
	0,11,0,0,CONTINUEWIDTH-2,0,CONTINUEWIDTH-2,11,CONTINUEWIDTH-1,11,
	CONTINUEWIDTH-1,0,CONTINUEWIDTH-1,11,1,11,1,0
};

struct Border continueborder= {
	0,0,
	1,0,
	JAM1,
	sizeof(continuebordxy)/4,	/* Questo calcola automaticamente la lunghezza */
	continuebordxy,			/* dell'array precedente */
	NULL
};


struct IntuiText continuetext =
{
	1,0,JAM1,
	(CONTINUEWIDTH-80)/2,2,
	&tanormal,
	(UBYTE *)"AutoScroll",
	NULL
};


struct Gadget continuegadg =
{
	NULL,
	460,180,
	CONTINUEWIDTH,12,
	GADGHCOMP,
	GADGIMMEDIATE | TOGGLESELECT,
	BOOLGADGET,
	(APTR)&continueborder,
	NULL,
	&continuetext,
	NULL,
	NULL,
	10,
	NULL
};




/* Definizione della finestra */
struct NewWindow nw =
{
	0,0,640,256,
	-1,-1,
	CLOSEWINDOW | GADGETDOWN | GADGETUP | MOUSEMOVE,
	WINDOWCLOSE | WINDOWDRAG | WINDOWDEPTH | SMART_REFRESH | NOCAREREFRESH,
	NULL,
	NULL,
	(UBYTE *)"Gadget proporzionali",
	NULL,
	NULL,
	0,0,0,0,
	WBENCHSCREEN
};








main()
{
struct IntuiMessage mymsg;
SHORT gadsel = 0;
extern struct Window *window;
extern GADG *scrollbar[5];


openall();	/* apre la finestra, disegna i gadget, ecc. */


FOREVER
{
/* se 'AutoScroll' e` attivo, muove i gadget, altrimenti aspetta un messaggio */
	if (continuegadg.Flags & SELECTED) autoscroll();
	else WaitPort(window->UserPort);

	switch (MyGetMsg(window->UserPort,&mymsg))
	{
	case CLOSEWINDOW:
		cleanup(RETURN_OK);
		break;
	case GADGETDOWN:
		gadsel = (((GADG *)mymsg.IAddress)->GadgetID);
		break;
	case GADGETUP:
	case MOUSEMOVE:
		switch (gadsel)
		{
		case 1:
			movebugbar(BarPos(100,1,BARINFO(scrollbar[0])->HorizPot));
			printbugbarpos();
			break;
		case 2:
			printbugbarpos();
			break;
		case 5:
			printstrings();
			break;
		}

		if (mymsg.Class == GADGETUP) gadsel = 0;
		break;
	}
}
}




/* muove i due gadget sulla destra della finestra. */
VOID autoscroll(VOID)
{
LONG t;
static LONG inc1 = 1,inc2 = 1;
extern struct Window *window;
extern GADG *scrollbar[5];

/* temporizza con il pennello elettronico */
WaitBOVP((struct ViewPort *)ViewPortAddress(window));

Forbid();

/* calcola la posizione attuale, la incrementa, e calcola il nuovo valore di Pot */
t = BarPos(AUTO_LINES,AUTO_SIZE,BARINFO(scrollbar[2])->VertPot);
if (t == AUTO_LINES - AUTO_SIZE) inc1 = -1;
else if (t == 0) inc1 = 1;

t += inc1;
t = SetPos(AUTO_LINES,AUTO_SIZE,t);
MoveScrollBar(scrollbar[2],window,NULL,t);


t = BarPos(AUTO_LINES,AUTO_SIZE,BARINFO(scrollbar[3])->VertPot);
if (t == AUTO_LINES - AUTO_SIZE) inc2 = -1;
else if (t == 0) inc2 = 1;

t += inc2;
t = SetPos(AUTO_LINES,AUTO_SIZE,t);
MoveDoubleScrollBar(scrollbar[3],window,NULL,t);

Permit();
}




/* scrive i parametri piu` importanti della scroll bar che mostra il bug */
/* descritto nell'articolo */
VOID printbugbarpos(VOID)
{
char buf[80];
extern struct Window *window;
extern GADG *scrollbar[5];

sprintf(buf,"GadgetLeftEdge:%2d  HorizPot:%5ld  KnobLeftEdge:%3d",
		scrollbar[1]->LeftEdge,(LONG)BARINFO(scrollbar[1])->HorizPot,
		BARKNOB(scrollbar[1])->LeftEdge);

MyText(window->RPort,buf,10,scrollbar[1]->TopEdge - 4);
}




/* riposiziona la scroll bar che mostra il bug di Intuition */
VOID movebugbar(SHORT x)
{
extern struct Window *window;
extern GADG *scrollbar[5];


RemoveGList(window,scrollbar[1],1L);
scrollbar[1]->LeftEdge = x;
AddGList(window,scrollbar[1],-1L,1L,NULL);
RefreshGList(scrollbar[1],window,NULL,1L);

SetAPen(window->RPort,0L);
SetDrMd(window->RPort,JAM1);

/* le due RectFill() servono per ripulire lo spazio lasciato libero dallo */
/* spostamento della scroll bar. Dovrebbero essere un po' piu` 'intelligenti'. */
if (scrollbar[1]->LeftEdge >= 2)
	RectFill(window->RPort,2L,scrollbar[1]->TopEdge,scrollbar[1]->LeftEdge - 1,
			scrollbar[1]->TopEdge + scrollbar[1]->Height);

RectFill(window->RPort,scrollbar[1]->LeftEdge + scrollbar[1]->Width,
		scrollbar[1]->TopEdge,560L,scrollbar[1]->TopEdge + scrollbar[1]->Height);
}




/* Scrive sullo schermo la lista di stringhe in alto a sinistra. */
/* legge automaticamente da scrollbar[4] la posizione. */
/* Questa funzione dovrebbe essere MOLTO piu` sofisticata, usare ScrollRast() ecc. */
VOID printstrings(VOID)
{
ULONG pos;
SHORT t;
char buf[16];
extern struct Window *window;
extern GADG *scrollbar[5];


pos = BarPos(STRING_LINES,STRING_SIZE,BARINFO(scrollbar[4])->VertPot);

for (t = pos;t < pos + STRING_SIZE;t++)
{
	sprintf(buf,"String %2d",t);
	MyText(window->RPort,buf,scrollbar[4]->LeftEdge - 80,scrollbar[4]->TopEdge + 6 + (t - pos) * 8);
}
}




/* scrive una stringa di testo nella RastPort specificata, alla posizione (x,y) */
VOID MyText(struct RastPort *rp,BYTE *txt,SHORT x,SHORT y)
{
SetAPen(rp,1L);
SetBPen(rp,0L);
SetDrMd(rp,JAM2);
Move(rp,x,y);
Text(rp,txt,strlen(txt));
}




/* Prende un messaggio dalla MsgPort (se ce n'e` uno), e lo copia in 'msg'. */
/* Ritorna il codice del messaggio, o zero se nessuno. E` garantito che il */
/* campo msg->Class sia uguale al valore ritornato. */
/* Eventuali sequenze di messaggi MOUSEMOVE vengono ridotte a un solo messaggio. */
ULONG MyGetMsg(struct MsgPort *port,struct IntuiMessage *msg)
{
register struct IntuiMessage *imsg;

do
{
	if (imsg = (struct IntuiMessage *)GetMsg(port))
	{
		CopyMem((char *)imsg,(char *)msg,(LONG)sizeof(*msg));
		ReplyMsg((struct Message *)imsg);
	}
	else msg->Class = 0;

/* se questo e` un MOUSEMOVE e il prossimo lo stesso, leggi il prossimo */
} while (msg->Class == MOUSEMOVE && (imsg = (struct IntuiMessage *)port->mp_MsgList.
		lh_Head)->ExecMessage.mn_Node.ln_Succ &&imsg->Class == MOUSEMOVE);

return(msg->Class);
}



/* Questa funzione alloca memoria per un gadget proporzionale e lo crea secondo */
/* i parametri specificati nella chiamata a funzione. La quantita` di memoria */
/* allocata viene memorizzata nel campo UserData per essere usata da FreeGadget(). */
/* Il gadget creato viene automaticamente aggiunto alla finestra e visualizzato. */
/* Per essere veramente completa, questa funzione dovrebbe permettere di */
/* specificare posizioni e dimensioni negative che andrebbero interpretate come */
/* relative alle dimensioni della finestra. */
GADG *CreateScrollBar(struct Window *wind,SHORT x,SHORT y,SHORT w,SHORT h,USHORT hpot,USHORT vpot,USHORT hbody,USHORT vbody,USHORT flags,USHORT id)
{
register GADG *gadg;
register PINF *pi;
register IMGE *im;
register LONG gadsize;

gadsize = sizeof(GADG) + sizeof(PINF) + sizeof(IMGE);

if (gadg = AllocMem(gadsize,MEMF_CLEAR | MEMF_PUBLIC))
{
	pi = (PINF *)(gadg + 1);
	im = (IMGE *)(pi + 1);

	gadg->LeftEdge = x;
	gadg->TopEdge = y;
	gadg->Width = w;
	gadg->Height = h;
	gadg->Flags = GADGHCOMP;
	gadg->Activation = GADGIMMEDIATE | FOLLOWMOUSE | RELVERIFY;
	gadg->GadgetType = PROPGADGET;
	gadg->GadgetRender = (APTR)im;
	gadg->SpecialInfo = (APTR)pi;
	gadg->GadgetID = id;
	gadg->UserData = (APTR)gadsize;

	pi->Flags = flags | AUTOKNOB;
	pi->HorizPot = hpot;
	pi->VertPot = vpot;
	pi->HorizBody = hbody;
	pi->VertBody = vbody;

	AddGList(wind,gadg,-1L,1L,NULL);
	RefreshGList(gadg,wind,NULL,1L);
}

return(gadg);
}




/* Questa funzione alloca memoria per una coppia di gadget proporzionali come */
/* spiegato nell'articolo e la crea secondo i parametri specificati nella */
/* chiamata a funzione. La quantita` di memoria allocata viene memorizzata nel */
/* campo UserData per essere usata da FreeGadget(). */
/* I gadget creati vengono automaticamente aggiunti alla finestra e visualizzati. */
/* La coppia andra` sempre considerata un corpo unico, e in caso di RemoveGList() */
/* o AddGList() su questa, il numero di gadget nela funzione dovra` sempre essere */
/* (almeno) 2. */
/* Per essere veramente completa, questa funzione dovrebbe permettere di */
/* specificare posizioni e dimensioni negative che andrebbero interpretate come */
/* relative alle dimensioni della finestra. */
GADG *CreateDoubleScrollBar(struct Window *wind,SHORT x,SHORT y,SHORT w,SHORT h,USHORT hpot,USHORT vpot,USHORT hbody,USHORT vbody,USHORT flags,USHORT id)
{
register GADG *gadg,*gadg1;
register PINF *pi;
register IMGE *im;
register LONG gadsize;

gadsize = 2 * (sizeof(GADG) + sizeof(PINF) + sizeof(IMGE));

if (gadg = AllocMem(gadsize,MEMF_CLEAR | MEMF_PUBLIC))
{
	pi = (PINF *)(gadg + 1);
	im = (IMGE *)(pi + 1);
	gadg1 = (GADG *)(im + 1);

	gadg->NextGadget = gadg1;
	gadg->LeftEdge = x;
	gadg->TopEdge = y;
	gadg->Width = w;
	gadg->Height = h;
	gadg->Flags = GADGHCOMP;
	gadg->Activation = GADGIMMEDIATE | FOLLOWMOUSE | RELVERIFY;
	gadg->GadgetType = PROPGADGET;
	gadg->GadgetRender = (APTR)im;
	gadg->SpecialInfo = (APTR)pi;
	gadg->GadgetID = id;
	gadg->UserData = (APTR)gadsize;

	pi->Flags = flags | AUTOKNOB;
	pi->HorizPot = hpot;
	pi->VertPot = vpot;
	pi->HorizBody = hbody;
	pi->VertBody = vbody;

	AddGList(wind,gadg,-1L,1L,NULL);
	RefreshGList(gadg,wind,NULL,1L);


	gadg1->LeftEdge = x + pi->LeftBorder;
	gadg1->TopEdge = y + pi->TopBorder;
	gadg1->Width = pi->CWidth - 2 * pi->LeftBorder;
	gadg1->Height = pi->CHeight - 2 * pi->TopBorder;
	gadg1->GadgetType = PROPGADGET;

	pi = (PINF *)(gadg1 + 1);
	im = (IMGE *)(pi + 1);

	gadg1->GadgetRender = (APTR)im;
	gadg1->SpecialInfo = (APTR)pi;

	pi->Flags = flags | AUTOKNOB | PROPBORDERLESS;
	pi->HorizPot = hpot;
	pi->VertPot = vpot;
	pi->HorizBody = hbody;
	pi->VertBody = vbody;

	AddGList(wind,gadg1,-1L,1L,NULL);
	RefreshGList(gadg1,wind,NULL,1L);
}

return(gadg);
}





/* Libera la memoria allocata per un gadget. Chiamare questa funzione solo DOPO */
/* un RemoveGList() o la chiusura della finestra!!!! */
/* E` lecito chiamare questa funzione con argomento nullo, nel qual caso si ha */
/* un ritorno immediato. */
VOID FreeGadget(GADG *gadg)
{
if (gadg) FreeMem(gadg,(LONG)gadg->UserData);
}




/* Sposta la manopola di un gadget proporzionale alla posizione specificata. */
VOID MoveScrollBar(GADG *bar,struct Window *wind,USHORT hpot,USHORT vpot)
{
register PINF *pi;

Forbid();

if (!(bar->Flags & SELECTED))	/* muovi la barra solo se non e` agganciata */
{
	pi = BARINFO(bar);

	NewModifyProp(bar,wind,NULL,pi->Flags,hpot,vpot,pi->HorizBody,pi->VertBody,1L);
}

Permit();
}



/* Sposta la manopola di un gadget doppio alla posizione specificata. */
/* Attenzione: si suppone che il gadget BORDERLESS sia consecutivo a quello */
/* specificato nella chiamata. */
VOID MoveDoubleScrollBar(GADG *bar1,struct Window *wind,USHORT hpot,USHORT vpot)
{
register PINF *pi;
register GADG *bar2 = bar1->NextGadget;

Forbid();

if (!(bar1->Flags & SELECTED))	/* muovi la barra solo se non e` agganciata */
{
	pi = BARINFO(bar2);

	/* questo serve per il kick2.0 */
	BARKNOB(bar2)->TopEdge = BARKNOB(bar1)->TopEdge;

	NewModifyProp(bar2,wind,NULL,pi->Flags,hpot,vpot,pi->HorizBody,pi->VertBody,1L);

	/* aggiorna anche i campi del gadget con bordo */
	BARINFO(bar1)->VertPot = pi->VertPot;
	BARKNOB(bar1)->TopEdge = BARKNOB(bar2)->TopEdge;
}

Permit();
}



#asm
; Questa funzione calcola la posizione indicata dalla scroll bar
;
; position = BarPos(ULONG numlines,SHORT pagesize,USHORT pot)
;    D0                     D0             D1          D2
;
; numlines = numero totale di linee
; pagesize = numero di linee visualizzate sullo schermo
; pot = HorizPot o VertPot dalla struttura PropInfo

	xdef	_BarPos
_BarPos:
	movem.l	d2/d3,-(sp)

;	move.l	12(sp),d0	;se il compilatore non puo` passare parametri nei
;	move.w	16(sp),d1	;registri, togliere il punto e virgola da
;	move.w	18(sp),d2	;davanti a queste linee

	ext.l	d1	;azzera la word alta di d1 (si suppone d1 < 8000)
	sub.l	d1,d0	;d0 = numlines - pagesize = maxline
	bhi	1$	;se pagesize >= numlines,
	moveq	#0,d0	;la posizione e` per forza 0, carica in d0 e esci
	bra	5$

1$	moveq	#0,d3
	not.w	d3	;carica FFFF in d3
	divu	d3,d0	;divide maxline per FFFF
	bvc	2$	;un overflow puo` avvenire solo se d0 >= FFFF0000
	move.w	d2,d1
	swap	d1	;in tal caso, d1 = pot * 10000
	bra	3$

2$	move.w	d0,d1	;se non c'era overflow,
	swap	d0	;resto in d0 e quoziente in d1
	mulu	d2,d1	;d1 = Q * pot
3$	mulu	d2,d0	;d0 = R * pot
	divu	d3,d0	;d0 = (R * pot) / FFFF
	tst.l	d0	;se il resto e` >= 8000
	bpl	4$
	addq.w	#1,d0	;arrotonda all'intero superiore
4$	and.l	d3,d0	;elimina il resto, non ci serve piu`
	add.l	d1,d0	;somma il tutto, finito
5$	movem.l	(sp)+,d2/d3
	rts




; Questa funzione calcola il valore da mettere nel campo Pot
;
; pot = SetPos(ULONG numlines,SHORT pagesize,ULONG pos)
;  D0                  D0             D1         D2
;
; numlines = numero totale di linee
; pagesize = numero di linee visualizzate sullo schermo
; pos = posizione attuale

	xdef	_SetPos:
_SetPos:
	movem.l	d2-d4,-(sp)

;	move.l	16(sp),d0	;se il compilatore non puo` passare parametri nei
;	move.w	20(sp),d1	;registri, togliere il punto e virgola da
;	move.l	22(sp),d2	;davanti a queste linee

	ext.l	d1	;azzera la word alta di d1 (si suppone d1 < 8000)
	sub.l	d1,d0	;d0 = numlines - pagesize = maxline
	bhi	1$	;se pagesize >= numlines,
	moveq	#0,d0	;pot e` per forza 0, carica in d0 e esci
	bra	10$

1$	cmp.l	d2,d0	;se pos > numlines (strano)
	bcc	2$
	moveq	#-1,d0	;pot va messo al massimo, cioe` FFFF
	bra	10$

2$	move.l	d0,d1
	swap	d1
	tst.w	d1	;se il divisore e` un LONG,
	bne	3$	;salta alla routine di divisione
	move.l	d2,d1
	swap	d1	;d1 = d2 * 10000
	sub.l	d2,d1	;d1 = d1 - d2 = d2 * FFFF
	divu	d0,d1	;esegue la divisione
	move.l	d1,d2
	clr.w	d2	;resto in d2
	swap	d2
	bra	8$	;e salta al codice di chiusura

3$	move.w	d2,d3
	swap	d3
	clr.w	d3
	move.l	d2,d4	;in queste righe moltiplica d2 per FFFF e pone
	clr.w	d2	;il risultato in d2/d3
	swap	d2
	sub.l	d4,d3
	bcc	4$
	subq	#1,d2
4$	moveq	#0,d1
	moveq	#31,d4	;ciclo da ripetere 32 volte
5$	add.l	d1,d1	;raddoppia il quoziente
	add.l	d3,d3	;e il dividendo
	addx.l	d2,d2
	bcs	6$	;se c'e` stato un riporto, si puo` sicuramente sottrarre
	cmp.l	d2,d0	;altrimenti controlla che dividendo > divisore
	bhi	7$
6$	sub.l	d0,d2	;sottrae il divisore
	addq.w	#1,d1	;e incrementa il risultato
7$	dbf	d4,5$	;loop
8$	lsr.l	#1,d0	;dimezza il divisore
	cmp.l	d0,d2	;e controlla se il resto e` maggiore
	bls	9$
	addq.w	#1,d1	;se si`, arrotonda all'intero superiore
9$	moveq	#0,d0
	move.w	d1,d0	;risultato in d0, fine
10$	movem.l	(sp)+,d2-d4
	rts




; Questa funzione calcola il valore da mettere nel campo Body
;
; body = SetBody(ULONG numlines,SHORT pagesize)
;  D0                    D0             D1
;
; numlines = numero totale di linee
; pagesize = numero di linee visualizzate sullo schermo

	xdef	_SetBody:
_SetBody:
	movem.l	d2-d3,-(sp)

;	move.l	12(sp),d0	;se il compilatore non puo` passare parametri nei
;	move.w	16(sp),d1	;registri, togliere il punto e virgola

	ext.l	d1	;questa funzione e` molto simile a SetPos(), non
	cmp.l	d0,d1	;ripetero` i commenti gia` fatti
	bcs	1$
	moveq	#-1,d0
	bra	9$
1$	move.l	d1,d2
	swap	d1
	sub.l	d2,d1
	move.l	d0,d2
	swap	d2
	tst.w	d2
	bne	2$
	divu	d0,d1
	move.l	d1,d2
	clr.w	d2
	swap	d2
	bra	7$

2$	moveq	#0,d2	;a differenza di SetPos(), qui il dividendo e` di soli
	swap	d1	;32 bit, quindi risparmio un registro (e una somma nel
	move.w	d1,d2	;ciclo) e tengo il risultato in d1 assieme a una parte
	clr.w	d1	;del divisore
	moveq	#15,d3
4$	add.l	d1,d1
	addx.l	d2,d2
	bcs	5$
	cmp.l	d2,d0
	bhi	6$
5$	sub.l	d0,d2
	addq.w	#1,d1
6$	dbf	d3,4$
7$	lsr.l	#1,d0
	cmp.l	d0,d2
	bls	8$
	addq.w	#1,d1
8$	moveq	#0,d0
	move.w	d1,d0
9$	movem.l	(sp)+,d2-d3
	rts
#endasm





/* Apre la finestra, aggiunge i gadget ecc. */
VOID openall(VOID)
{
extern struct Window *window;
extern GADG *scrollbar[5];

if (!(GfxBase = OpenLibrary("graphics.library",0L)))
	cleanup(RETURN_FAIL);

if (!(IntuitionBase = OpenLibrary("intuition.library",33L)))
	cleanup(RETURN_FAIL);

if (!(window = OpenWindow(&nw)))
	cleanup(RETURN_FAIL);

if (!(scrollbar[0] = CreateScrollBar(window,10,210,200,10,0,NULL,
		SetBody(100,1),NULL,FREEHORIZ,1)))
	cleanup(RETURN_FAIL);

if (!(scrollbar[1] = CreateScrollBar(window,0,240,450,10,0x7fff,NULL,
		SetBody(3,1),NULL,FREEHORIZ,2)))
	cleanup(RETURN_FAIL);

if (!(scrollbar[2] = CreateScrollBar(window,570,20,20,230,NULL,NULL,NULL,
		SetBody(AUTO_LINES,AUTO_SIZE),FREEVERT,3)))
	cleanup(RETURN_FAIL);

if (!(scrollbar[3] = CreateDoubleScrollBar(window,600,20,20,230,NULL,NULL,NULL,
		SetBody(AUTO_LINES,AUTO_SIZE),FREEVERT,4)))
	cleanup(RETURN_FAIL);

if (!(scrollbar[4] = CreateScrollBar(window,100,30,20,8*STRING_SIZE,NULL,0x7fff,
		NULL,SetBody(STRING_LINES,STRING_SIZE),FREEVERT,5)))
	cleanup(RETURN_FAIL);

AddGList(window,&continuegadg,-1L,1L,NULL);
RefreshGList(&continuegadg,window,NULL,1L);


/* Inizializza il display con tutto il testo necessario */
printbugbarpos();
printstrings();

MyText(window->RPort,"Programma dimostrativo",216,80);
MyText(window->RPort,"dei gadget proporzionali",208,90);
MyText(window->RPort,"Scritto da Nicola Salmoria",200,110);
MyText(window->RPort,"per la rivista Amiga Magazine",188,120);
MyText(window->RPort,"Vedere l'articolo",236,140);
MyText(window->RPort,"per maggiori informazioni",204,150);
}



/* Chiude tutto e esce */
VOID cleanup(SHORT code)
{
SHORT j;

if (window) CloseWindow(window);
for (j = 0;j < 5;j++) FreeGadget(scrollbar[j]);
if (IntuitionBase) CloseLibrary(IntuitionBase);
if (GfxBase) CloseLibrary(GfxBase);

exit(code);
}
