/* interface.c */

/* the actual interface to the player routine
 */
 
 
/* $Author: Espie $
 * $Date: 91/05/14 02:29:48 $
 * $Revision: 1.25 $
 * $Log:	interface.c,v $
 * Revision 1.25  91/05/14  02:29:48  Espie
 * The auto resize was no longer working... Latest
 * version hadn't been tested. Put the code in order.
 * Added a temporary fix to the asynchronous resize
 * problem (window jumping all other the place when
 * doing fast resizes).
 * 
 * I should rewrite part of the code to get rid of
 * relative coordinates once and for all (maybe...)
 * 
 * Revision 1.24  91/05/12  19:53:21  Espie
 * pause handled like other gadgets, no longer special case.
 * 
 * Revision 1.23  91/05/12  15:57:15  Espie
 * Minor changes.
 * Did not allocate enough points 2*number of point = coordinates,
 * so it did abort mysteriously, only when optimized.. sigh.
 * 
 * Revision 1.22  91/05/10  00:03:55  Espie
 * 
 * Corrected NewPropInfo bug.
 * 
 * Revision 1.21  91/05/09  17:35:32  Espie
 * pref_xxx added,
 * minor modifications in gadget handling.
 * hook for appwindow: returns the window now,
 * events.c takes care of the signal mask.
 * 
 * Revision 1.20  91/05/08  15:50:50  Espie
 * Volume slider now working.
 * Warning ! Nothing to add another slider right now.
 * 
 * Revision 1.19  91/05/07  12:12:22  Espie
 * *** empty log message ***
 * 
 * Revision 1.18  91/05/07  02:53:53  Espie
 * Added smart ``resize window''.
 * Will now move the window if anything is amiss.
 * Experimented with the GetScreenDrawInfo aspect ratio.
 * 
 * Revision 1.17  91/05/06  23:35:47  Espie
 * Completely cleaned up all codes,
 * added new entries, like temporary_title/restore_title,
 * suppressed obsolete ones. Still needs to get gadgets/borders...
 * tight.
 * 
 * Revision 1.16  91/05/05  04:01:26  Espie
 * Added the void_title() call.
 * Changed minor details.
 * 
 * Revision 1.15  91/05/03  02:49:14  Espie
 * Added sizing gadget. Don't like it, so commented it out
 * (Intuition spends its time trashing its own borders... not neat.
 * Also changed order of gadgets. Load is most important gadget,
 * so it is first. Restart and pause are arranged for graphical
 * symbols (sigh...) |<   and   ''   .
 * 
 * Revision 1.14  91/05/03  00:42:15  Espie
 * Cosmetic changes
 * 
 * Revision 1.13  91/05/02  23:29:50  Espie
 * Cosmetic changes. Starting message when no song is loaded yet.
 * 
 * Revision 1.12  91/05/02  11:20:13  Espie
 * Handle a variable number of gadget much better.
 * Added banner, start_player() conditionnally if there IS a song to play.
 * 
 * Revision 1.11  91/05/02  01:30:28  Espie
 * Many changes in the handle events part, simpler than before.
 * 
 * Revision 1.10  91/05/01  00:49:32  Espie
 * Fonts working, resizing ok.
 * 
 * Revision 1.9  91/04/30  16:51:03  Espie
 * Added a real button for speed.
 * Handle font changes a little better...
 * 
 * Revision 1.8  91/04/30  01:48:11  Espie
 * Some minor problems with tempo changing not when it should, fixed.
 * 
 * Revision 1.7  91/04/30  00:36:17  Espie
 * Stable version III.
 * 
 * Revision 1.6  91/04/30  00:24:56  Espie
 * New reset_tempo() command, for using the current state of the speed
 * setting when swapping songs.
 * 
 * Revision 1.5  91/04/29  23:52:41  Espie
 * Added possibility to load files on the fly.
 * Surprisingly simple, even if limited right now
 * (I need the possibility to spawn my subtasks,
 * that would GREATLY simplify most of the program).
 * 
 * Revision 1.4  91/04/29  15:04:13  Espie
 * Small modifications to take advantage of the new interface/audio_soft/audio_hard.
 * Corrected a misconception concerning toggleselect gadgets:
 * check their state BEFORE waiting for them, and don't assume
 * you can keep a copy of their internal state.
 * Guaranteed way to get out of touch with intuition...
 * 
 * Revision 1.3  91/04/29  13:03:16  Espie
 * Interfaced to new audio_soft.
 * 
 * Revision 1.2  91/04/29  03:52:29  Espie
 * A start for a real interface.
 * Displays the song title and patterns,
 * has boolean gadgets for pause, restart, change speed.
 * Misses some imagery yet...
 * 
 * Revision 1.1  91/04/29  02:23:15  Espie
 * Initial revision
 * 
 *
 */


#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <dos/dos.h>
#include <custom/cleanup.h>
#include <custom/graphics.h>
#include <assert.h>
#include "proto.h"
#include "banner.c"


	/* see <custom/graphics.h> 
	*/
#ifdef SIZE_GADGET
struct saveclip *saveclip;
#endif

/* this is all the parameters we might want to change
 * depending on the current screen type
 */

/* the colors. */
UBYTE TEXT, SHINE, SHADOW, HILIGHT, BACKGROUND;

/* the highlighting method for boolean gadgets */
ULONG GAD_HILIGHT;

/* the spacing between graphics elements */
int xspace, yspace;


struct Window *window;
struct Screen *screen;
struct RastPort *rastport;
int sys_interwidth, sys_baseline, scr_interwidth, scr_baseline;


void setup_environment(void)
	{
		/* get all we can */
		screen = window->WScreen;
		rastport= window->RPort;
		sys_interwidth = rastport->TxHeight + 1;
		sys_baseline = rastport->TxBaseline;
		scr_interwidth = screen->RastPort.TxHeight + 1;
		scr_baseline = screen->RastPort.TxBaseline;
		
		/* colors have changed under 2.0, these are the old Pen Numbers,
		 * and should provide reasonable defaults */
		SHINE = 1;
		SHADOW = 2;
		TEXT = 2;
		HILIGHT = 1;
		BACKGROUND = 0;
		GAD_HILIGHT = GADGHIMAGE;
		
		xspace = 4;
		yspace = 4;
		if (running_20())
         	{
			struct DrawInfo *di;
			di = GetScreenDrawInfo(window->WScreen);
			if (di->dri_Version >= 1)
				{
					if (di->dri_Flags & DRIF_NEWLOOK)
						{
       		     		TEXT = di->dri_Pens[textPen];
            				HILIGHT = di->dri_Pens[hilighttextPen];
							SHADOW = di->dri_Pens[shadowPen];
							SHINE = di->dri_Pens[shinePen];
							BACKGROUND = di->dri_Pens[backgroundPen];
							if (SHADOW == SHINE)
								GAD_HILIGHT = GADGHCOMP;
						}
					xspace = 108/di->dri_Resolution.X;
					yspace = 108/di->dri_Resolution.Y;
				}
			FreeScreenDrawInfo(window->WScreen, di);
         }
		else
				/* non interlace screen */
			if (screen->Height < 300)
				yspace = 2;
	}


#define MAX_VOLUME 16
#define REAL_MAX_VOLUME 257

/***
 *
 *		The gadget creation routines.
 *
 *		The basic function are ``object declarations''.
 *		Since these structures don't change very often,
 *		storage is static right now
 *
 ***/

/* modify the following constants if you want to add some stuff.
 */
 
#define N_GAD 6
#define N_BORDER 6
#define N_COORD 36
#define N_INTUI 6
#define M_NUMBER 11
#define N_PROP 1
#define N_IMAGE 0

/* the actual gadget numbers */

#define LOAD_NUMBER 0
#define RESTART_NUMBER 1
#define PAUSE_NUMBER 2
#define SPEED_NUMBER 3
#define MODE_NUMBER 4
#define VOLUME_NUMBER 5

LOCAL struct Gadget g_array[N_GAD], *gad;
LOCAL struct Border b_array[N_BORDER], *border;
LOCAL struct IntuiText lab_array[N_INTUI], *label, *status;
LOCAL SHORT XY_array[N_COORD], *XY;
LOCAL struct PropInfo p_array[N_PROP], *prop;
LOCAL struct Image i_array[N_IMAGE + N_PROP], *image;

/* the fixed messages */
#define M_PAL 0
#define M_SPEED 5
#define M_RESTART 5
#define M_PAUSE 6
#define M_LOAD 7
#define M_STD 8
#define MAX_SPEED 5
#define MAX_MODE 3

LOCAL char *that[M_NUMBER] = {"PAL", "NTSC", "Fast", "Scan", "Slow", "Restart", 
		"Pause", "Load", "Std", "Old", "New"};

/* the changing message */

/* 30 chars for the song name, + 8 for the numbers --> 40 is plenty enough. */
#define L_STATUS 0
LOCAL char buffer[40];

/* to compute the extent of the display elements */
LOCAL int xmax, ymax;

/* we have to offset everything by the window border */
#define relx(a) (window->BorderLeft + (a))
#define rely(a) (window->BorderTop + (a))


/***
 *
 *		 the text display functions 
 *
 ****/


LOCAL void setup_status_line(void)
   {
		label->FrontPen = TEXT;
      label->BackPen = BACKGROUND;
	   	/* need JAM 2 for fast updates */
      label->DrawMode = JAM2;
      label->LeftEdge = relx(4 * xspace);
      label->TopEdge = rely(yspace);
      label->ITextFont = NULL;
      label->IText = buffer;
      label->NextText = NULL;
	   assert(label - lab_array < N_INTUI);
	   status = label++;
   }     

/* erase_color()/restore_text(): for cleaning up some text.
 */

LOCAL int oldcolor;

LOCAL void erase_color(struct IntuiText *t)
   {
      oldcolor = t->FrontPen;
      t->FrontPen = 0; 
   }
   
LOCAL void restore_color(struct IntuiText *t)
   {
      t->FrontPen = oldcolor;
   }


LOCAL void display_status()
	{
		PrintIText(rastport, status, 0, 0);
	}


/* external hook for audio_soft.c: show whether we have the audio currently,
 * or not.
 */
void have_audio(BOOL yep)
	{
		if (yep)
			status->FrontPen = HILIGHT;
		else
			status->FrontPen = TEXT;
		display_status();
	}
	 	  
/***
 *
 *		The gadget creation functions
 *
 ***/
 


/* init_gads: setup everything for gadget creation */
LOCAL void init_gads(void)
   {
   int i;
      for (i = 0; i < N_GAD; i++)
         {
            g_array[i].NextGadget = g_array+i+1;
            g_array[i].GadgetID = i;
            g_array[i].Flags =  0;
         }
      g_array[N_GAD-1].NextGadget = NULL;
      gad = g_array;
      XY = XY_array;
      border = b_array;
	   label = lab_array;
	   prop = p_array;
	   image = i_array;
      xmax = 0;
      ymax = 0;
   }



/* border creation functions */

LOCAL void point(SHORT x, SHORT y)
   {
      *XY++ = x;
      *XY = y;
      assert(XY - XY_array < N_COORD);
	   XY++;
   }

LOCAL struct Border *allocate_border(int npoints, int pen)
	{
      border->LeftEdge = 0;
      border->TopEdge = 0;
      border->FrontPen = pen;
      border->XY = XY;
      border->Count = npoints;
      border->DrawMode = JAM1;
      border->NextBorder = NULL;
	   assert(border - b_array < N_BORDER);
      return border++;
	}		   


/* setting gadget parameters */	


LOCAL void set_pos(int x, int y)
   {
      gad->LeftEdge = x;
      gad->TopEdge = y;
   }
   
LOCAL void set_size(int width, int height)
   {
   int xx, yy;
      gad->Width = width;
      gad->Height = height;
      xx = gad->Width + gad->LeftEdge;
      yy = gad->Height + gad->TopEdge;
      if (xx > xmax)
         xmax = xx;
      if (yy > ymax)
         ymax = yy;
   }

LOCAL void set_bool(void)
   {
      gad->GadgetType = BOOLGADGET;
      gad->Flags = GAD_HILIGHT;
      gad->Activation = GADGIMMEDIATE | RELVERIFY;
   }

LOCAL void set_toggle(void)
   {
      gad->GadgetType = BOOLGADGET;
      gad->Flags = GAD_HILIGHT;
      gad->Activation = GADGIMMEDIATE | TOGGLESELECT;
   }

FORWARD LOCAL struct Border *create_box(int pen1, int pen2);

LOCAL void reduce_container(void)
	{
		gad->LeftEdge++;
		gad->TopEdge++;
		gad->Width -= 2;
		gad->Height -= 2;
	}
	
LOCAL void set_prop(int maximum, int initial)
	{
		gad->UserData = create_box(SHADOW, SHINE);
		reduce_container();
		gad->GadgetType = PROPGADGET;
		gad->SpecialInfo = prop;
		gad->Flags = GADGHNONE;
		gad->Activation = RELVERIFY|GADGIMMEDIATE;
		gad->GadgetRender = image;
		prop->Flags = AUTOKNOB|FREEVERT|PROPBORDERLESS;
		prop->VertBody = MAXPOT / (maximum+1);
		prop->VertPot = (initial * MAXPOT) / maximum;
		prop->HorizBody = MAXPOT;
	}

LOCAL void refresh_prop(struct Gadget *g)
	{
		DrawBorder(rastport, g->UserData, g->LeftEdge-1, g->TopEdge-1);
	}
		
/* the gadget labels */

LOCAL void init_label(void)
	{
		label->FrontPen = TEXT;
		label->BackPen = BACKGROUND;
		label->DrawMode = JAM1;
		label->LeftEdge = xspace;
		label->TopEdge = yspace;
		label->ITextFont = window->WScreen->Font;
		label->NextText = NULL;
	}
				
LOCAL void setup_label(int n)
	{
		init_label();
		label->IText = that[n];
		gad->GadgetText = label;
		assert(label - lab_array < N_INTUI);
		label++;
	}

/* each button can have the same size */

LOCAL int size_labels(void)
	{
	int l, needed, i;
		needed = 0;
		init_label();
		for (i = 0; i < M_NUMBER; i++)
			{
				label->IText = that[i];
				l = IntuiTextLength(label);
				if (l > needed)
					needed = l;
			}
		return needed;
	}

/* more intricate gadget creation functions */

/* this is an ``highlight box'' ala 2.0 */

LOCAL struct Border *create_box(int pen1, int pen2)
   {
	struct Border *first, *second;
		first = allocate_border(3, pen1);
      	point(0, gad->Height - 1);
      	point(gad->Width - 1, gad->Height - 1);
      	point(gad->Width - 1, 0);
		second = allocate_border(3, pen2);
      	second->NextBorder = first;
		point(0, gad->Height - 1);
		point(0, 0);
		point(gad->Width - 1, 0);
		return second;
   }     

LOCAL void set_box(void)
   {
      gad->GadgetRender = create_box(SHADOW, SHINE);
      gad->SelectRender = create_box(SHINE, SHADOW);
   }

/* the boxes are shared between gadgets */
LOCAL void same_box(struct Gadget *other)
   {
      gad->GadgetRender = other->GadgetRender;
      gad->SelectRender = other->SelectRender;
   }     

#if 0
/* this stuff might be used to add a graphic to some gadgets... later */

LOCAL void add_render(struct Border *border)
	{
		border->NextBorder = gad->GadgetRender;
		gad->GadgetRender = border;
	}
	
LOCAL void add_select(struct Border *border)
	{
		border->NextBorder = gad->SelectRender;
		gad->SelectRender = border;
	}
#endif

LOCAL void next_gadget(void)
   {
		assert(gad - g_array < N_GAD);
		gad++;
   }

   
LOCAL void setup_gadgets(BOOL req_avail)
   {  
   int width, height, x, y;
      /* I want some room for a message 
	    * and for a proportional gadget
		*/
      	x = relx(4 * xspace);
      	y = rely(2 * yspace + sys_interwidth);
	   
      	height = scr_interwidth + 2 * yspace;
	   	width = size_labels() + 2 * xspace;
		  	/* these need to get first */
		set_size(width, height);
		set_box();
	   		/* requester gadget */
		if (req_avail)
			{
		      	set_pos(x, y);
		      	set_size(width, height);
		      	set_bool();
				setup_label(M_LOAD);
      			next_gadget();
				x += width + xspace;
				set_size(width, height);
				same_box(g_array);
			}
	   		/* restart gadget */
      set_pos(x, y);
      set_bool();
	   setup_label(M_RESTART);
      next_gadget();
	   
	   		/* pause gadget */
      x += width + xspace;
      set_pos(x, y);
      set_size(width, height);
      	set_toggle();
       same_box(g_array);
	    setup_label(M_PAUSE);
       next_gadget();
		
	   		/* speed gadget */
      	x += width + xspace;
      	set_pos(x, y);
      	set_size(width, height);
		set_bool();
      	same_box(g_array);
		setup_label(M_PAL);
      	next_gadget();
			/* ``mode'' gadget */
		x += width + xspace;
		set_pos(x, y);
		set_size(width, height);
		set_bool();
		same_box(g_array);
		setup_label(M_STD);
		next_gadget();
			/* volume gadget */
		x = relx(xspace);
		y = rely(yspace);
		set_pos(x, y);
		set_size(2 * xspace, ymax - y);
		set_prop(MAX_VOLUME, MAX_VOLUME);
		next_gadget();
		xmax+= xspace;
		ymax+= yspace;
   }



/* changing the window size and title...
 * this stuff can also deal with resize gadgets.
 */


LOCAL int xstart = 80, ystart = 40;
LOCAL int xcurrent, ycurrent;

LOCAL char *window_title = "Experiment IV";


#ifdef SIZE_GADGET
LOCAL void delimit(void)
	{
		WindowLimits(window, 0, 0, xcurrent + window->BorderRight, 
			ycurrent + window->BorderBottom);
	}
#endif

/* safeSizeWindow deals of lots of special cases.
 * It tries to enlarge (shrink ?) window by dx, dy,
 * moving it around if it can't do it any other way,
 * respecting the screen size as it goes.
 * You usually pass w->Width and w->Height as current width,
 * current height.
 * However, for frequent resizes, it is better to keep track
 * of the size the window should be, because SizeWindow is actually
 * asynchronous.
 */
 
LOCAL void safeSizeWindow(struct Window *w, int dx, int dy, int cw, int ch)
	{
	int nw, nh, ddx, ddy;
		if (dx > 0)
			{
				nw = cw + dx;
				if (nw > w->WScreen->Width)
					{
						nw = w->WScreen->Width;
						dx = nw - w->Width;
					}
				ddx = w->WScreen->Width - w->LeftEdge - nw;
				if (ddx > 0)
					ddx = 0;
			}
		else
			ddx = 0;
		if (dy > 0)
			{
				nh = ch + dy;
				if (nh > w->WScreen->Height)
					{
						nh = w->WScreen->Height;
						dy = nh - w->Height;
					}
				ddy = w->WScreen->Height - w->TopEdge - nh;
				if (ddy > 0)
					ddy = 0;
			}
		else
			ddy = 0;
		if (ddx || ddy)
			MoveWindow(w, ddx, ddy);
		if (dx || dy)
			SizeWindow(w, dx, dy);
	}
	
LOCAL void add_borders(void)
   {
      safeSizeWindow(window, 
         window->BorderRight + xmax - xstart, 
         window->BorderBottom + ymax - ystart, xstart, ystart);
      xcurrent = xmax;
      ycurrent = ymax;
#ifdef SIZE_GADGET
	   delimit();
#endif
   }

LOCAL void resize(int xnew, int ynew)
   {
      safeSizeWindow(window, xnew - xcurrent, ynew - ycurrent, 
	   			xcurrent + window->BorderRight, 
				ycurrent + window->BorderBottom);
      xcurrent = xnew;
      ycurrent = ynew;
#ifdef SIZE_GADGET
	   delimit();
#endif
   }
          
LOCAL void setup_title(void)
	{
	   SetWindowTitles(window, -1, banner);
	   ToClean3(SetWindowTitles, window ,0, 0);
	}
	
void temporary_title(char *change)
	{
		SetWindowTitles(window, change, -1);
	}
	
void restore_title(void)
	{
		SetWindowTitles(window, window_title, -1);
	}
	
LOCAL void setup_all()
	{
		init_gads();
		setup_status_line();
		setup_gadgets(init_requester(window));
		add_borders();
#ifdef SIZE_GADGET
		saveclip = cliptoborder(window, NULL);
#endif
		setup_title();
		AddGList(window, g_array, ~0, gad - g_array, NULL);
		ToClean3(RemoveGList, window, g_array, gad - g_array);
		refresh_prop(g_array+VOLUME_NUMBER);
		start_player();
		RefreshGList(g_array, window, NULL, gad - g_array);
	}


/* externally accessible function */
		
struct Window *init_interface(int xoffset, int yoffset)
   {
   struct NewWindow template;
      template.Type = WBENCHSCREEN;
      template.TopEdge = yoffset; 
      template.LeftEdge = xoffset;
      template.DetailPen = -1;
      template.BlockPen = -1;
      template.IDCMPFlags = CLOSEWINDOW 
                     | REFRESHWINDOW
#ifdef SIZE_GADGET
					  | NEWSIZE
#endif
                     | GADGETDOWN
					  | GADGETUP;
      template.Flags  = WINDOWDEPTH 
                  | WINDOWCLOSE
                  | WINDOWDRAG
#ifdef SIZE_GADGET
				   | WINDOWSIZING
#endif
				   | SIZEBBOTTOM
                  | SIMPLE_REFRESH;
      template.FirstGadget = NULL;
      template.CheckMark = NULL;
      template.Title = window_title;
      template.MinWidth = 0;
      template.MinHeight = 0;
      template.MaxWidth = ~0;
      template.MaxHeight = ~0;
      template.Width = xstart;
      template.Height = ystart;
      window = OpenWindow(&template);
      if (window)
			ToClean(CloseWindow, window);
		else
       	mayPanic("Could not open window");
		setup_environment();
	   	/* setup the window like it should be */
		setup_all();
		
		no_title();
		return window; 
	}
	
/***
 *
 *		Status line management 
 *
 ***/

LOCAL void change_title(void)
	{ 
 	int needed;
		needed = TextLength(rastport, buffer, strlen(buffer)) + 5 * xspace;
		resize(needed > xmax ? needed : xmax, ymax);
		display_status();
	}
			
void new_title(int pos, int len, char *title)
   {
		erase_color(status);
		display_status();
		restore_color(status);
		sprintf(buffer, "%3d:%-3d %s \0", pos, len, title);
		change_title();
   }
         
void update_title(int pos, int len, char *title)
   {
		sprintf(buffer, "%3d:%-3d %s \0", pos, len, title);
		display_status();
   }

void no_title(void)
	{
		erase_color(status);
		display_status();
		restore_color(status);
	   	sprintf(buffer, "No song loaded\0");
		change_title();
	}

LOCAL int current_mode = 0;

	
LOCAL void reset_mode()
	{
		if (current_mode >= MAX_MODE)
			current_mode = 0;
      erase_color(g_array[MODE_NUMBER].GadgetText);
      RefreshGList(g_array+MODE_NUMBER, window, NULL, 1);
	   g_array[MODE_NUMBER].GadgetText->IText = that[M_STD + current_mode];
      restore_color(g_array[MODE_NUMBER].GadgetText);
      RefreshGList(g_array+MODE_NUMBER, window, NULL, 1);
      set_mode(current_mode);
	}
	
LOCAL void toggle_mode()
	{
		current_mode++;
		reset_mode();
	}
				
void pref_mode(int mode)
	{
		current_mode = mode;
		reset_mode();
	}
	
/* internal (and external) tempo management */

LOCAL int current_speed = 0;

LOCAL int tempo[] =  {256, 213, 170, 110, 550};
LOCAL int effect[] = {256, 213, 170, 20, 550};
	
/* in some conditions, we need to put back the tempo to its
 * natural value (i.e., after a pause)
 */
 
void reset_tempo(void)
   {
      if (current_speed >= MAX_SPEED)
      		current_speed = 0;
    	erase_color(g_array[SPEED_NUMBER].GadgetText);
    	RefreshGList(g_array+SPEED_NUMBER, window, NULL, 1);
		g_array[SPEED_NUMBER].GadgetText->IText = that[M_PAL + current_speed];
    	restore_color(g_array[SPEED_NUMBER].GadgetText);
    	RefreshGList(g_array+SPEED_NUMBER, window, NULL, 1);
      set_tempo(tempo[current_speed], effect[current_speed]);
   }
   
void pref_speed(int speed)
	{
		current_speed = speed;
		reset_tempo();
	}
	
LOCAL void toggle_tempo(void)
   {
         current_speed++;
      reset_tempo();
   }


LOCAL void reset_volume(void)
	{
		set_volume(REAL_MAX_VOLUME * (MAXPOT - prop->VertPot)
			/MAXPOT);
	}

void pref_volume(int volume)
	{
		NewModifyProp(g_array+VOLUME_NUMBER, window, NULL, prop->Flags,
			prop->HorizPot, MAXPOT - volume*MAXPOT/100, prop->HorizBody,
			prop->VertBody, VOLUME_NUMBER);
		reset_volume();
	}

/* the proper event handling */

LOCAL BOOL aborted;

LOCAL void add_to_idcmp(ULONG val)
	{
		ModifyIDCMP(window, window->IDCMPFlags | val);
	}
	
LOCAL void remove_from_idcmp(ULONG val)
	{
		ModifyIDCMP(window, window->IDCMPFlags & ~val);
	}
	
/* some gadgets only react to gadget up, other to gadget down,
 * some to both !
 */
 
LOCAL void gadget_up(ULONG id)
	{
		switch(id)
			{
			case RESTART_NUMBER:
				launch_play(0);
				break;
			case LOAD_NUMBER:
				try_change_song();
				break;
			case VOLUME_NUMBER:
				remove_from_idcmp(INTUITICKS);
				break;
			default:
				break;
			}
	}

LOCAL void gadget_down(ULONG id)
	{
		switch(id)
			{
			case SPEED_NUMBER:
				toggle_tempo();
				break;
			case PAUSE_NUMBER:
		    	if (g_array[PAUSE_NUMBER].Flags & SELECTED)
					stop_player();
				else
					start_player();
				break;
			case MODE_NUMBER:
				toggle_mode();
				break;
					/* the volume gadget turns on ``busy update'' 
					 * while selected. If I add more prop gadgets,
					 * I will have to keep track of which ones to
					 * update.
					 */
			case VOLUME_NUMBER:
				add_to_idcmp(INTUITICKS);
				break;
					/* right now, the state of the pause gadget is
					 * constantly tested... because ``external events''
					 * (like the availability of a song) might
					 * change the behaviour to follow.
					 */
			default:
				 break;
			}
	}
	

LOCAL void process_message(struct IntuiMessage *msg)
   {
   static ULONG class;
   static void *object;
   static struct Gadget *gadget;
		class = msg->Class;
		object = msg->IAddress;
		ReplyMsg((struct Message *)msg);
		switch(class)
       	{
         	case CLOSEWINDOW:
				aborted = TRUE;
            	break;
         	case GADGETDOWN:
            	gadget = (struct Gadget *)object;
				gadget_down(gadget->GadgetID);
				break;
			case GADGETUP:
            	gadget = (struct Gadget *)object;
				gadget_up(gadget->GadgetID);
            	break;
			case INTUITICKS:
				reset_volume();
				break;
#ifdef SIZE_GADGET
			case NEWSIZE:
				xcurrent = window->Width - window->BorderRight;
				ycurrent = window->Height - window->BorderBottom;
				recliptoborder(saveclip);
				break;
#endif
         case REFRESHWINDOW:
            BeginRefresh(window);
			 display_status();
			refresh_prop(g_array+VOLUME_NUMBER);
            EndRefresh(window, TRUE);
            break;
         default:
            break;
         }
   }

/* access through this one is needed by the filerequester */

void message_interface(struct IntuiMessage *msg)
	{
		process_message(msg);
	}
	
void handle_interface(void)
   {
   struct IntuiMessage *msg;
		while(msg = (struct IntuiMessage *)GetMsg(window->UserPort))
			process_message(msg);
   }

/* concerns events.c exclusively */

BOOL did_abort(void)
	{
		return aborted;
	}
	
void clear_abort(void)
	{
		aborted = FALSE;
	}

	
