#include "sysheaders.h"
#include "cdpanel.h"
#include "CompactPlayer.h"

/********************************************************
 * Main GUI
 */

#define CDPanelObject   NewObject( CDPanelClass, NULL /* ) open parentheses annoy */

struct Gadget *GList[GG_MAX];

struct Screen *Scr;
struct DrawInfo *Dri;
struct Window *Win;
struct Menu *MyMenuStrip;
APTR VisInfo;
Object *WinObj;
struct Gadget *MainLayout;
STRPTR Screen;

static UWORD chip forwardData[26] =
{
	/* Plane 0 */
	0x0000,0x0000,0x0000,0x0000,0x0410,0x0000,0x0618,0x0000,
	0x071C,0x0000,0x079E,0x0000,0x07DF,0x0000,0x079E,0x0000,
	0x071C,0x0000,0x0618,0x0000,0x0410,0x0000,0x0000,0x0000,
	0x0000,0x0000
};

static struct Image ForwardButton =
{
	0, 0,	  /* LeftEdge, TopEdge */
	20, 13, 1,  /* Width, Height, Depth */
	forwardData,	/* ImageData */
	0x0001, 0x0000, /* PlanePick, PlaneOnOff */
	NULL		/* NextImage */
};

static UWORD chip playData[26] =
{
	/* Plane 0 */
	0x0000,0x0000,0x0000,0x0000,0x1003,0xDE00,0x1C03,0xDE00,
	0x1F03,0xDE00,0x1FC3,0xDE00,0x1FF3,0xDE00,0x1FC3,0xDE00,
	0x1F03,0xDE00,0x1C03,0xDE00,0x1003,0xDE00,0x0000,0x0000,
	0x0000,0x0000
};

static struct Image PlayButton =
{
	0, 0,	  /* LeftEdge, TopEdge */
	26, 13, 1,  /* Width, Height, Depth */
	playData,   /* ImageData */
	0x0001, 0x0000, /* PlanePick, PlaneOnOff */
	NULL		/* NextImage */
};

static UWORD chip rewindData[26] =
{
	/* Plane 0 */
	0x0000,0x0000,0x0000,0x0000,0x0082,0x0000,0x0186,0x0000,
	0x038E,0x0000,0x079E,0x0000,0x0FBE,0x0000,0x079E,0x0000,
	0x038E,0x0000,0x0186,0x0000,0x0082,0x0000,0x0000,0x0000,
	0x0000,0x0000
};

static struct Image RewindButton =
{
	0, 0,	  /* LeftEdge, TopEdge */
	20, 13, 1,  /* Width, Height, Depth */
	rewindData, /* ImageData */
	0x0001, 0x0000, /* PlanePick, PlaneOnOff */
	NULL		/* NextImage */
};

static UWORD chip stopData[26] =
{
	/* Plane 0 */
	0x0000,0x0000,0x0000,0x0000,0x1FF0,0x2000,0x1FF0,0x7000,
	0x1FF0,0xF800,0x1FF1,0xFC00,0x1FF3,0xFE00,0x1FF0,0x0000,
	0x1FF3,0xFE00,0x1FF3,0xFE00,0x1FF3,0xFE00,0x0000,0x0000,
	0x0000,0x0000
};

static struct Image StopButton =
{
	0, 0,	  /* LeftEdge, TopEdge */
	26, 13, 1,  /* Width, Height, Depth */
	stopData,   /* ImageData */
	0x0001, 0x0000, /* PlanePick, PlaneOnOff */
	NULL		/* NextImage */
};

typedef enum { M_SelDev = 1, M_EditTitles, M_Iconify, M_Quit } MenuIDs;

struct NewMenu CompactMenu[] =
{
	NM_TITLE,   MENU_PROJECT,	NULL, 0, 0, NULL,
	NM_ITEM,	MENU_DEVICE,	NULL,  0, 0, (APTR)M_SelDev,
	NM_ITEM,	MENU_TITLES,	NULL,  0, 0, (APTR)M_EditTitles,
	NM_ITEM,	NM_BARLABEL,	NULL, 0, 0, NULL,
	NM_ITEM,	MENU_ICONIFY,	NULL,  0, 0, (APTR)M_Iconify,
	NM_ITEM,	MENU_QUIT,		NULL,  0, 0, (APTR)M_Quit,
	NM_END,	 NULL,		   NULL, 0, 0, NULL
};

struct TextAttr CompactAttr = { NULL, 0, FS_NORMAL, FPF_PROPORTIONAL };
struct TextAttr PanelAttr = { NULL, 0, FS_NORMAL, 0L };
struct TextFont *CompactFont;
struct TextFont *PanelFont;
struct TextFont *ScreenFont;

struct List dummyList;

/* handler functions for the buttons, since I want to act on them from
 * rawkey messages too. Gadgets don't support rawkey command keys (as of yet
 * anyway) because it is generally a bad practise to bind a rawkey to a gadget. */
static void
SeekCD(ULONG id)
{
  /* notifications from the rewind/fast forward buttons */
	ULONG status;
	static struct timeval tv1, tv2, tv3;

	GetSysTime(&tv1);
	tv3 = tv1;
	SubTime(&tv3, &tv2);

	GetAttr(CDPANEL_Status, GList[G_Panel], &status);

	if ((status == CDP_PLAYING || status == CDP_SEEKING)
		&& (tv3.tv_secs || tv3.tv_micro >= 190000))
	{
		tv2 = tv1;					 /* only seek four times per second */
		switch (id)
		 {
		 case G_Backward:
			 CD_Seek(-2);
			 break;

		 case G_Forward:
			 CD_Seek(2);
			 break;
		 }
		if (status == CDP_PLAYING)
			SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_SEEKING, TAG_END);
	}
}

static void
PlayCD(void)
{
	ULONG status;

	GetAttr(CDPANEL_Status, GList[G_Panel], &status);

	if (status == CDP_PLAYING || status == CDP_SEEKING)
	{
		SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PAUSED, TAG_END);
		CD_PauseResume(0x00);
	}
	else if (status == CDP_PAUSED)
	{
		SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PLAYING, TAG_END);
		CD_PauseResume(0x01);
	}
	else if (status == CDP_STOPPED)
	{
		ULONG track;

		SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PLAYING, TAG_END);
		GetAttr(LISTBROWSER_Selected, GList[G_List], &track);
		if (track++ > 100)
			track = 1;
		CD_Play(track);
		AbortIO(TimerIO);
	}
	else if (status == CDP_EJECTED || status == CDP_EMPTY)
	{
		CD_Eject(0x00);
		Delay(20);					 /* give it a chance to settle. I really hate these kind of things */
		if (Tracks = CD_ReadTOC())
			CD_Play(1);
	}
}

static void
StopCD(void)
{
	ULONG status;

	GetAttr(CDPANEL_Status, GList[G_Panel], &status);

	JustStarted = FALSE;

	if (status == CDP_STOPPED || status == CDP_EMPTY || status == CDP_EJECTED)
	{
		SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_EJECTED, TAG_END);
		CD_Eject(0x01);
	}
	else
	{
		SetGadgetAttrs(GList[G_List], Win, NULL, LISTBROWSER_Selected, ~0, TAG_END);
		SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_STOPPED, TAG_END);
		CD_Stop();
		AbortIO(TimerIO);
	}
}

static void
SelectCD(LONG dir)
{
	LONG track;
	ULONG status;

	GetAttr(CDPANEL_Track,  GList[G_Panel], (ULONG *) & track);
	GetAttr(CDPANEL_Status, GList[G_Panel], &status);

	if (dir > 0)
	{
		 if (track < 100)
		 {
			 track++;
			 if (track > Tracks)
				 track = Tracks;
		 }
		 else
			 track = 1;
	}
	else
	{
		if (track > 100)
			track = Tracks;
		track--;
		if (track < 1)
			track = 1;
	}
	SetGadgetAttrs(GList[G_List], Win, NULL, LISTBROWSER_Selected, track-1, LISTBROWSER_MakeVisible, track-1, TAG_END);
	SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Track, track, TAG_END);

	if (status == CDP_PLAYING || status == CDP_SEEKING)
	{
		 CD_Play(track);
		 AbortIO(TimerIO);
	}
}

static struct Hook IDCMPHook;

static ULONG __asm __saveds
IDCMPHookFunc(register __a0 struct Hook * hook, register __a2 Object * WinObj, register __a1 struct IntuiMessage *msg)
{
	/* this function is hooked from windowclass to grab some IDCMP messages */

	switch (msg->Class)
	{
	case IDCMP_IDCMPUPDATE:
	{
		ULONG id = GetTagData(GA_ID, 0, msg->IAddress);

		if (FindTagItem(GA_Selected, msg->IAddress) && (id == G_Backward || id == G_Forward))
		{
			SeekCD(id);
		}
		break;
	}

	case IDCMP_RAWKEY:
		switch (msg->Code)
		{
		case 0x43:  /* enter */
		case 0x44:  /* return */
			if (!(msg->Qualifier & IEQUALIFIER_REPEAT))
			{
				DoGadgetMethod(GList[G_Stop], Win, NULL, GM_KEYACTIVE, NULL, msg, (ULONG)'\n' );
				StopCD();
			}
			break;
		case 0xc3:
		case 0xc4:
			DoGadgetMethod(GList[G_Stop], Win, NULL, GM_KEYINACTIVE, NULL, msg, (ULONG)'\n', 0 );
			break;

		case 0x40:  /* space */
			if (!(msg->Qualifier & IEQUALIFIER_REPEAT))
			{
				DoGadgetMethod(GList[G_Play], Win, NULL, GM_KEYACTIVE, NULL, msg, (ULONG)' ' );
				PlayCD();
			}
			break;
		case 0xc0:
			DoGadgetMethod(GList[G_Play], Win, NULL, GM_KEYINACTIVE, NULL, msg, (ULONG)' ', 0 );
			break;

		case 0x4f:  /* cursor left */
			DoGadgetMethod(GList[G_Backward], Win, NULL, GM_KEYACTIVE, NULL, msg, (ULONG)'<' );
			SeekCD(G_Backward);
			break;

		case 0xcf:  /* cursor left (keyup)*/
			SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PLAYING, TAG_END);
			DoGadgetMethod(GList[G_Backward], Win, NULL, GM_KEYINACTIVE, NULL, msg, (ULONG)'>', 0 );
			break;

		case 0x4e:  /* cursor right */
			DoGadgetMethod(GList[G_Forward], Win, NULL, GM_KEYACTIVE, NULL, msg, (ULONG)'>' );
			SeekCD(G_Forward);
			break;

		case 0xce:  /* cursor right (keyup) */
			SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PLAYING, TAG_END);
			DoGadgetMethod(GList[G_Forward], Win, NULL, GM_KEYINACTIVE, NULL, msg, (ULONG)'>', 0 );
			break;

		case 0x4c:  /* cursor up */
			SelectCD(-1);
			break;

		case 0x4d:  /* cursor down */
			SelectCD(+1);
			break;

		case 0x45|0x80: /* escape up */
			done = TRUE;
			break;
			
		case 0x01:
		case 0x1d:
			CD_Play(1);
			break;
			
		case 0x02:
		case 0x1e:
			CD_Play(2);
			break;
			
		case 0x03:
		case 0x1f:
			CD_Play(3);
			break;
			
		case 0x04:
		case 0x2d:
			CD_Play(4);
			break;
			
		case 0x05:
		case 0x2e:
			CD_Play(5);
			break;
			
		case 0x06:
		case 0x2f:
			CD_Play(6);
			break;
			
		case 0x07:
		case 0x3d:
			CD_Play(7);
			break;
			
		case 0x08:
		case 0x3e:
			CD_Play(8);
			break;
			
		case 0x09:
		case 0x3f:
			CD_Play(9);
			break;
			
		case 0x0a:
		case 0x0f:
			CD_Play(10);
			break;
		}
		break;
	}
	return 0;
}

struct Gadget *
CreateMainLayout( void )
{
	struct Image *i;

	SetAttrs( GList[G_Panel], 
			CDPANEL_NoBorder, FALSE, 
			TAG_END );

	return HGroupObject, Offset(1,1,1,1), Spacing(1),
		GA_TextAttr, & CompactAttr,
		GA_DrawInfo, Dri,
		LAYOUT_DeferLayout, TRUE,
		LAYOUT_AddChild, VGroupObject, Spacing(1),

			/* Layout hierarchies are so hard to explain.. we have the
			 * window split to the status panel side and the listview side,
			 * with four buttons below the status panel. */

			StartMember, GList[G_Panel],
				CHILD_NoDispose, TRUE,
				CHILD_CacheDomain, FALSE,

			StartHGroup,
				LAYOUT_SpaceInner, FALSE,

				StartMember, GList[G_Backward] = ButtonObject,
					GA_Image, i = & RewindButton,
					GA_RelVerify, TRUE,
					GA_ID, G_Backward,
					ICA_TARGET, ICTARGET_IDCMP,
					BUTTON_FillPen, Dri->dri_Pens[BACKGROUNDPEN],
					CLASSACT_CommKey, "<",
					End,
					CHILD_MinWidth, i->Width,
					CHILD_WeightMinimum, TRUE,

				StartMember, GList[G_Play] = ButtonObject,
					GA_Image, i = & PlayButton,
					GA_RelVerify, TRUE,
					GA_ID, G_Play,
					BUTTON_FillPen, Dri->dri_Pens[BACKGROUNDPEN],
					End,
					CHILD_MinWidth, i->Width,
					CHILD_WeightMinimum, TRUE,

				StartMember, GList[G_Forward] = ButtonObject,
					GA_Image, i = & ForwardButton,
					GA_RelVerify, TRUE,
					GA_ID, G_Forward,
					ICA_TARGET, ICTARGET_IDCMP,
					BUTTON_FillPen, Dri->dri_Pens[BACKGROUNDPEN],
					CLASSACT_CommKey, ">",
					End,
					CHILD_MinWidth, i->Width,
					CHILD_WeightMinimum, TRUE,

				StartMember, GList[G_Stop] = ButtonObject,
					GA_Image, i = & StopButton,
					GA_RelVerify, TRUE,
					GA_ID, G_Stop,
					BUTTON_FillPen, Dri->dri_Pens[BACKGROUNDPEN],
					End,
					CHILD_MinWidth, i->Width,
					CHILD_WeightMinimum, TRUE,
				End,
				CHILD_WeightedHeight, 0,
			End,
			CHILD_WeightedWidth, 0,

		StartVGroup, Spacing(1),

			/* This is the listview group, with two buttons above it */

			StartHGroup, Spacing(1),
				LAYOUT_EvenSize, TRUE,

				StartMember, GList[G_Titles] = ButtonObject,
					GA_Text, GS(B_TITLES),
					GA_TextAttr, & CompactAttr,
					GA_RelVerify, TRUE,
					GA_ID, G_Titles,
					End,

				StartMember, GList[G_Program] = ButtonObject,
					GA_Text, GS(B_PROGR),
					GA_TextAttr, & CompactAttr,
					GA_RelVerify, TRUE,
					GA_ID, G_Program,
					End,
				End,
				/* force the button smaller than it tells is the minimum size.
				 * This gets rid of a few pixels that aren't needed, to really
				 * minimize the layout.. It's called _Compact_Player, after all.
				 */
				CHILD_WeightedHeight, 0,

			StartMember, GList[G_List] = ListBrowserObject,
				LISTBROWSER_Labels, TrackList ? TrackList : &dummyList,
				LISTBROWSER_VerticalProp, FALSE,
				LISTBROWSER_ShowSelected, TRUE,
				GA_ReadOnly, TrackList ? FALSE : TRUE,
				GA_TextAttr, & CompactAttr,
				GA_RelVerify, TRUE,
				GA_ID, G_List,
				End,
			End,
		End;
}

struct Gadget *
CreateZippedLayout( void )
{
	memset(&GList[G_Backward], 0, (ULONG)&GList[GG_MAX1]-(ULONG)&GList[G_Backward]);

	/* By sharing the status panel gadget between the zoomed and normal layouts,
	 * we retain the play graph of the gadget. This is allowed by V41 layout.gadget,
	 * which has the NoDispose attribute for suppressing automatic child dispose.
	 */

	SetAttrs( GList[G_Panel], 
			CDPANEL_NoBorder, TRUE, 
			TAG_END );

	return HGroupObject,
		GA_DrawInfo, Dri,
		LAYOUT_DeferLayout, TRUE,
		LAYOUT_FixedHoriz, FALSE,
		LAYOUT_FixedVert, FALSE,

		StartMember, GList[G_Panel],
			CHILD_NoDispose, TRUE,
		   	CHILD_CacheDomain, FALSE,

		End;
}

struct Window *
OpenMainWindow(ULONG X, ULONG Y)
{
	if (!ButtonBase)				   /* force it open */
		return NULL;

	if (Win)
		return Win;

	NewList(&dummyList);

	if (!(Scr = LockPubScreen(Screen)))
		Scr = LockPubScreen(NULL);

	if (Scr)
	{
		ScreenFont = OpenFont(Scr->Font);

		if (!CompactAttr.ta_Name)
		{
			CompactAttr = *Scr->Font;
		}
		if (!PanelAttr.ta_Name)
		{
			PanelAttr.ta_Name = GfxBase->DefaultFont->tf_Message.mn_Node.ln_Name;
			PanelAttr.ta_YSize = GfxBase->DefaultFont->tf_YSize;
		}

		if ((CompactFont = OpenDiskFont(&CompactAttr)) && (PanelFont = OpenDiskFont(&PanelAttr)))
		{
			if ((Dri = GetScreenDrawInfo(Scr)) && (VisInfo = GetVisualInfo(Scr, TAG_END)))
			{
				if ((MyMenuStrip = CreateMenus(CompactMenu, TAG_END)) &&
					LayoutMenus(MyMenuStrip, VisInfo, GTMN_NewLookMenus, TRUE, TAG_END))
				{
					struct IBox ZoomSize;

					GList[G_Panel] = CDPanelObject, CDPANEL_Status, CDP_EMPTY, GA_TextAttr, &PanelAttr, End;

					MainLayout = CreateZippedLayout();

					LayoutLimits(MainLayout, (struct LayoutLimits *) & ZoomSize, NULL, Scr);
					ZoomSize.Top = -1;
					ZoomSize.Left = -1;
					ZoomSize.Width += Scr->WBorLeft + Scr->WBorRight + 2;
					ZoomSize.Height += Scr->WBorTop + ScreenFont->tf_YSize + 1 + Scr->WBorBottom + 2;

					DisposeObject(MainLayout);

					MainLayout = CreateMainLayout();

					if (MainLayout)
					{
						struct DiskObject *icon = NULL;

						IDCMPHook.h_Entry = (HOOKFUNC) IDCMPHookFunc;
						IDCMPHook.h_SubEntry = NULL;
						IDCMPHook.h_Data = NULL;

						if (_WBenchMsg)
							icon = GetDiskObject(_WBenchMsg->sm_ArgList->wa_Name);
						if (!icon)
							icon = GetDiskObject("PROGDIR:CompactPlayer");

						if (icon)
						{
							icon->do_CurrentY = icon->do_CurrentX = NO_ICON_POSITION;
						}

						if (WinObj = WindowObject,
								WA_DepthGadget,		TRUE,
								WA_DragBar,			TRUE,
								WA_CloseGadget,		TRUE,
								WA_Activate,		TRUE,
								LayoutBase->cl_Lib.lib_Version >= 41 ?
							WA_Zoom : TAG_IGNORE,	&ZoomSize,	/* V41 layout.gadget knows CHILD_NoDispose */
								WA_Top,				Y,
								WA_Left,			X,
								WA_PubScreen,		Scr,
								WA_Title,			GS(COMPACTPLAYER),
								WA_ScreenTitle,		GS(TITLE_COPYRIGHT),
								WA_AutoAdjust,		TRUE,
								WINDOW_Layout,		MainLayout,
								WINDOW_IDCMPHook,	&IDCMPHook,
								WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE | IDCMP_RAWKEY,
								WA_IDCMP,			IDCMP_IDCMPUPDATE | IDCMP_RAWKEY,
								WINDOW_MenuStrip,	MyMenuStrip,
								WINDOW_AppPort,		AppPort,
								WINDOW_SharedPort,	WinPort,
								WINDOW_Icon,		icon, /* windowclass will free this */
								WINDOW_IconifyGadget, TRUE,
								TAG_END)
							)
						{
							Win = CA_OpenWindow(WinObj);

							return Win;
						}
						DisposeObject(MainLayout);
						MainLayout = NULL;
					}
					ErrorMsg(GS(GUI_FAIL));
					
					DisposeObject( GList[G_Panel] );
				}
			}
		}
		else
			ErrorMsg(GS(FONTS_FAIL));
	}
	return NULL;
}

void
CloseMainWindow(void)
{
	if (TrackList)
	{
		SetGadgetAttrs(GList[G_List], Win, NULL, LISTBROWSER_Labels, ~0, TAG_END);
		FreeBrowserNodes(TrackList);
		TrackList = NULL;
	}
	if (WinObj)
	{
		DisposeObject(WinObj);
		DisposeObject(GList[G_Panel]);
		MainLayout = NULL;
		WinObj = NULL;
		Win = NULL;
		memset(&GList[G_Panel], 0, (ULONG)&GList[GG_MAX1]-(ULONG)&GList[G_Panel]);
	}
	if (MyMenuStrip)
	{
		FreeMenus(MyMenuStrip);
		MyMenuStrip = NULL;
	}
	if (VisInfo)
	{
		FreeVisualInfo(VisInfo);
		VisInfo = NULL;
	}
	if (Dri)
	{
		FreeScreenDrawInfo(Scr, Dri);
		Dri = NULL;
	}
	if (Scr)
	{
		UnlockPubScreen(0, Scr);
		Scr = NULL;
	}
	if (CompactFont)
	{
		CloseFont(CompactFont);
		CompactFont = NULL;
	}
	if (PanelFont)
	{
		CloseFont(PanelFont);
		PanelFont = NULL;
	}
	if (ScreenFont)
	{
		CloseFont(ScreenFont);
		ScreenFont = NULL;
	}
}

void
Iconify(void)
{
	CA_Iconify( WinObj );
	CA_CloseWindow( ListWinObj );
	CA_CloseWindow( ConfWinObj );
	Win = ListWin = ConfWin = NULL;

	SetAttrs(WinObj, WA_PubScreen, NULL, TAG_END);
	SetAttrs(ListWinObj, WA_PubScreen, NULL, TAG_END);
	SetAttrs(ConfWinObj, WA_PubScreen, NULL, TAG_END);

	UnlockPubScreen( NULL, Scr );
}

void
UnIconify(void)
{
	if (!(Scr = LockPubScreen(Screen)))
		Scr = LockPubScreen(NULL);

	SetAttrs(WinObj, WA_PubScreen, Scr, TAG_END);
	SetAttrs(ListWinObj, WA_PubScreen, Scr, TAG_END);
	SetAttrs(ConfWinObj, WA_PubScreen, Scr, TAG_END);

	Win	 = CA_OpenWindow( WinObj );
	ListWin = CA_OpenWindow( ListWinObj );
	ConfWin = CA_OpenWindow( ConfWinObj );
}

void
MainWindowIDCMP()
{
	ULONG result;
	UWORD code;
	BOOL iconify = FALSE;

	while ((result = CA_HandleInput(WinObj, &code)) != WMHI_LASTMSG)
	{
		/* Windowclass translates key events to GADGETUPs on its own */

		switch (result & WMHI_CLASSMASK)
		{
		 case WMHI_CLOSEWINDOW:
			 done = TRUE;
			 break;

		 case WMHI_GADGETUP:
			switch (result & WMHI_GADGETMASK)
			 {
			 case G_Backward:
				 {
					 ULONG status;

					 /* only do this if not playing. During playing do a continous
					  * seek by listening to the IDCMP messages */

					 GetAttr(CDPANEL_Status, GList[G_Panel], &status);

					 if (status == CDP_STOPPED)
					 {
						SelectCD(-1);
					 }
					 else
					 {
						static struct timeval lasttime;
						struct timeval tv1, tv2;
						
						if (status == CDP_SEEKING)
							SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PLAYING, TAG_END);
					 	
					 	GetSysTime(&tv1);
					 	tv2 = tv1;
					 	SubTime(&tv2, &lasttime);
					 	lasttime = tv1;
					 	
					 	if (tv2.tv_secs == 0 && tv2.tv_micro <= 900000)
					 		SelectCD(-1);	/* doubleclick */
					 }
				 }
				 break;

			 case G_Play:
				 PlayCD(); /* see earlier in this file */
				 break;

			 case G_Forward:
				 {
					 ULONG status;

					 /* As with G_Backward */

					 GetAttr(CDPANEL_Status, GList[G_Panel], &status);

					 if (status == CDP_STOPPED)
					 {
						SelectCD(+1);
					 }
					 else
					 {
						static struct timeval lasttime;
						struct timeval tv1, tv2;
						
						if (status == CDP_SEEKING)
							SetGadgetAttrs(GList[G_Panel], Win, NULL, CDPANEL_Status, CDP_PLAYING, TAG_END);
					 	
					 	GetSysTime(&tv1);
					 	tv2 = tv1;
					 	SubTime(&tv2, &lasttime);
					 	lasttime = tv1;
					 	
					 	if (tv2.tv_secs == 0 && tv2.tv_micro <= 900000)
					 		SelectCD(+1);	/* doubleclick */
					 }
				 }
				 break;

			 case G_Stop:
				 StopCD(); /* see earlier */
				 break;

			 case G_Titles:
				 {
					 ULONG status;

					 /* open up the list list editor */

					 OpenListWindow(Win->LeftEdge + Win->Width, Win->TopEdge);

					 GetAttr(CDPANEL_Status, GList[G_Panel], &status);

					 if (status != CDP_EJECTED && status != CDP_EMPTY)
					 {
						 UpdateTitleEditor();
					 }
				 }
				 break;

			 case G_Program:
				/* program editor unimplemented */
				 break;

			 case G_List:
				/* user selected a track in the listview, jump to it and play */
				 CD_Play(code + 1);
				 AbortIO(TimerIO);
				 break;
			 }
			break;

		case WMHI_MENUPICK:
			code = result & WMHI_MENUMASK;
			while (code != MENUNULL)
			{
				struct MenuItem *mi = ItemAddress( MyMenuStrip, code );
				switch ((ULONG)GTMENUITEM_USERDATA( mi ))
				{
				case M_SelDev:
					OpenConfWindow(Win->LeftEdge, Win->TopEdge);
					break;

				case M_EditTitles:
					{
						ULONG status;

						OpenListWindow(Win->LeftEdge + Win->Width, Win->TopEdge);

						GetAttr(CDPANEL_Status, GList[G_Panel], &status);

						if (status != CDP_EJECTED && status != CDP_EMPTY)
						{
							UpdateTitleEditor();
						}
					}
					break;

				case M_Iconify:
					iconify = TRUE;
					break;

				case M_Quit:
					done = TRUE;
					break;
				}
				code = mi->NextSelect;
			}
			break;

		case WMHI_NEWSIZE:
			if (result & WMF_ZIPWINDOW)
			{
				struct Gadget *l;
				if (result & WMF_ZOOMED)
					l = CreateZippedLayout();
				else
					l = CreateMainLayout();
				if (l)
				{
					SetAttrs( WinObj, WINDOW_Layout, MainLayout = l, TAG_END );
				}
			}
			break;

		case WMHI_UNICONIFY:
			UnIconify();
			break;

		case WMHI_ICONIFY:
			iconify = TRUE;
			break;
		}
	}
	if (iconify)
	{
		Iconify();
	}
}
