/* This is an example of implementing Multi Select Gadtools
 * ListViews with OS3.0+.
 *
 * m goes down list
 * M up
 * Spaces toggles selection
 *
 * Note - The list view can not be drawn as disabled.
 *
 * mark@topic.demon.co.uk
 * mpaddock@cix.compulink.co.uk
 *
 * Use Freely!
 */

#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <string.h>

int __osversion=39;
/*
 *  Source machine generated by GadToolsBox V2.0b
 *  which is (c) Copyright 1991-1993 Jaba Development
 *
 *  GUI Designed by : M J P
 */

#define GetString( g )      ((( struct StringInfo * )g->SpecialInfo )->Buffer  )
#define GetNumber( g )      ((( struct StringInfo * )g->SpecialInfo )->LongInt )

#define GD_MList                               0

#define GDX_MList                              0

#define MultiList_CNT 1

extern struct IntuitionBase *IntuitionBase;
extern struct Library       *GadToolsBase;

extern struct Screen        *Scr;
extern UBYTE                 *PubScreenName;
extern APTR                  VisualInfo;
extern struct Window        *MultiListWnd;
extern struct Gadget        *MultiListGList;
extern struct IntuiMessage   MultiListMsg;
extern struct Gadget        *MultiListGadgets[1];
extern UWORD                 MultiListLeft;
extern UWORD                 MultiListTop;
extern UWORD                 MultiListWidth;
extern UWORD                 MultiListHeight;
extern UBYTE                *MultiListWdt;
extern struct TextAttr      *Font, Attr;
extern UWORD                 FontX, FontY;
extern UWORD                 OffX, OffY;
extern struct MinList        MList0List;
extern UWORD                 MultiListGTypes[];
extern struct NewGadget      MultiListNGad[];
extern ULONG                 MultiListGTags[];

extern int MListClicked( void );

extern int SetupScreen( void );
extern void CloseDownScreen( void );
extern int HandleMultiListIDCMP( void );
extern int MultiListCloseWindow(void);
extern int MultiListVanillaKey(void);
extern int OpenMultiListWindow( void );
extern void CloseMultiListWindow( void );

struct ExtNode {
  struct Node Node;
  BOOL Selected;
  BOOL Current;
};

/*
 *  Source machine generated by GadToolsBox V2.0b
 *  which is (c) Copyright 1991-1993 Jaba Development
 *
 *  GUI Designed by : M J P
 */


struct Screen         *Scr = NULL;
UBYTE                 *PubScreenName = NULL;
APTR                   VisualInfo = NULL;
struct Window         *MultiListWnd = NULL;
struct Gadget         *MultiListGList = NULL;
struct IntuiMessage    MultiListMsg;
struct Gadget         *MultiListGadgets[1];
UWORD                  MultiListLeft = 205;
UWORD                  MultiListTop = 155;
UWORD                  MultiListWidth = 172;
UWORD                  MultiListHeight = 75;
UBYTE                 *MultiListWdt = (UBYTE *)"Multi Select";
struct TextAttr       *Font, Attr;
UWORD                  FontX, FontY;
UWORD                  OffX, OffY;

extern struct MinList MList0List;

struct ExtNode MList0Nodes[] = {
	(struct Node *)&MList0Nodes[1], ( struct Node * )&MList0List.mlh_Head, 0, 0, "Entry0",FALSE,FALSE,
	(struct Node *)&MList0Nodes[2], (struct Node *)&MList0Nodes[0], 0, 0, "Entry1",FALSE,TRUE,
	(struct Node *)&MList0Nodes[3], (struct Node *)&MList0Nodes[1], 0, 0, "Entry2",TRUE,FALSE,
	(struct Node *)&MList0Nodes[4], (struct Node *)&MList0Nodes[2], 0, 0, "Entry3",TRUE,FALSE,
	(struct Node *)&MList0Nodes[5], (struct Node *)&MList0Nodes[3], 0, 0, "Entry4",FALSE,FALSE,
	(struct Node *)&MList0Nodes[6], (struct Node *)&MList0Nodes[4], 0, 0, "Entry5",FALSE,FALSE,
	(struct Node *)&MList0Nodes[7], (struct Node *)&MList0Nodes[5], 0, 0, "Entry6",FALSE,FALSE,
	(struct Node *)&MList0Nodes[8], (struct Node *)&MList0Nodes[6], 0, 0, "Entry7",FALSE,FALSE,
	(struct Node *)&MList0Nodes[9], (struct Node *)&MList0Nodes[7], 0, 0, "Entry8",FALSE,FALSE,
	( struct Node * )&MList0List.mlh_Tail, (struct Node *)&MList0Nodes[8], 0, 0, "Entry9",FALSE,FALSE};

#define ENTRYCOUNT 10
int CurEntry = 1;

struct MinList MList0List = {
	( struct MinNode * )&MList0Nodes[0], ( struct MinNode * )NULL, ( struct MinNode * )&MList0Nodes[9] };

UWORD MultiListGTypes[] = {
	LISTVIEW_KIND
};

struct NewGadget MultiListNGad[] = {
	3, 15, 167, 60, (UBYTE *)"_Multi Select List", NULL, GD_MList, PLACETEXT_ABOVE, NULL, (APTR)MListClicked
};

struct Hook				  HookList = {0};

ULONG MultiListGTags[] = {
	(GTLV_Labels), (ULONG)&MList0List, (GT_Underscore), '_', GTLV_CallBack,	(ULONG)&HookList, (TAG_DONE)
};

ULONG __saveds __asm HookEntryList(register __a0 struct Hook *Hook,
                     register __a2 struct ExtNode *ExtNode,
                     register __a1 struct LVDrawMsg *msg) {
  char buffer[2];
  ULONG len;
  WORD length;
  struct TextExtent textExtent;
  if (msg->lvdm_MethodID != LV_DRAW) {
    return LVCB_UNKNOWN;
  }
  switch(msg->lvdm_State) {
  case LVR_NORMAL:
  case LVR_NORMALDISABLED:
  case LVR_SELECTED:
  case LVR_SELECTEDDISABLED:
    Move(msg->lvdm_RastPort,msg->lvdm_Bounds.MinX,
         msg->lvdm_Bounds.MinY+msg->lvdm_RastPort->TxBaseline);
    length = TextLength(msg->lvdm_RastPort,"*",1);
	 if (ExtNode->Current) {
      buffer[0] = '*';
    }
    else {
      buffer[0] = ' ';
    }
    buffer[1]=0;
    len = TextFit(msg->lvdm_RastPort,ExtNode->Node.ln_Name,
          strlen(ExtNode->Node.ln_Name),&textExtent,NULL,1,
          msg->lvdm_Bounds.MaxX-msg->lvdm_Bounds.MinX+1-length,32767);
    if (!ExtNode->Selected) {
      SetABPenDrMd(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[TEXTPEN],
                   msg->lvdm_DrawInfo->dri_Pens[BACKGROUNDPEN],JAM2);
    }
    else {
      SetABPenDrMd(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[FILLTEXTPEN],
                   msg->lvdm_DrawInfo->dri_Pens[FILLPEN],JAM2);
    }
    Text(msg->lvdm_RastPort,buffer,1);
    if (msg->lvdm_RastPort->cp_x < msg->lvdm_Bounds.MinX+length) {
      if (!ExtNode->Selected) {
        SetAPen(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[BACKGROUNDPEN]);
      }
      else {
        SetAPen(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[FILLPEN]);
      }
      RectFill(msg->lvdm_RastPort,msg->lvdm_RastPort->cp_x,msg->lvdm_Bounds.MinY,
               msg->lvdm_Bounds.MinX-1+length,msg->lvdm_Bounds.MaxY);
    }
    Move(msg->lvdm_RastPort,msg->lvdm_Bounds.MinX+length,
         msg->lvdm_Bounds.MinY+msg->lvdm_RastPort->TxBaseline);
    if (!ExtNode->Selected) {
      SetABPenDrMd(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[TEXTPEN],
                   msg->lvdm_DrawInfo->dri_Pens[BACKGROUNDPEN],JAM2);
    }
    else {
      SetABPenDrMd(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[FILLTEXTPEN],
                   msg->lvdm_DrawInfo->dri_Pens[FILLPEN],JAM2);
    }
    Text(msg->lvdm_RastPort,ExtNode->Node.ln_Name,len);
    if (!ExtNode->Selected) {
      SetAPen(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[BACKGROUNDPEN]);
    }
    else {
      SetAPen(msg->lvdm_RastPort,msg->lvdm_DrawInfo->dri_Pens[FILLPEN]);
    }
    RectFill(msg->lvdm_RastPort,msg->lvdm_RastPort->cp_x,msg->lvdm_Bounds.MinY,
             msg->lvdm_Bounds.MaxX,msg->lvdm_Bounds.MaxY);
    break;
  default:
    break;
  }
  return LVCB_OK;
}


static UWORD ComputeX( UWORD value )
{
	return(( UWORD )((( FontX * value ) + 3 ) / 6 ));
}

static UWORD ComputeY( UWORD value )
{
	return(( UWORD )((( FontY * value ) + 5 ) / 10 ));
}

static void ComputeFont( UWORD width, UWORD height )
{
	Font = &Attr;
	Font->ta_Name = (STRPTR)Scr->RastPort.Font->tf_Message.mn_Node.ln_Name;
	Font->ta_YSize = FontY = Scr->RastPort.Font->tf_YSize;
	FontX = Scr->RastPort.Font->tf_XSize;

	OffX = Scr->WBorLeft;
	OffY = Scr->RastPort.TxHeight + Scr->WBorTop + 1;

	if ( width && height ) {
		if (( ComputeX( width ) + OffX + Scr->WBorRight ) > Scr->Width )
			goto UseTopaz;
		if (( ComputeY( height ) + OffY + Scr->WBorBottom ) > Scr->Height )
			goto UseTopaz;
	}
	return;

UseTopaz:
	Font->ta_Name = (STRPTR)"topaz.font";
	FontX = FontY = Font->ta_YSize = 8;
}

int SetupScreen( void )
{
	if ( ! ( Scr = LockPubScreen( PubScreenName )))
		return( 1L );

	ComputeFont( 0, 0 );

	if ( ! ( VisualInfo = GetVisualInfo( Scr, TAG_DONE )))
		return( 2L );

	return( 0L );
}

void CloseDownScreen( void )
{
	if ( VisualInfo ) {
		FreeVisualInfo( VisualInfo );
		VisualInfo = NULL;
	}

	if ( Scr        ) {
		UnlockPubScreen( NULL, Scr );
		Scr = NULL;
	}
}

typedef int (FPTR(void));

int HandleMultiListIDCMP( void )
{
	struct IntuiMessage	*m;
	int			(*func)(void);
	BOOL			running = TRUE;

	while( m = GT_GetIMsg( MultiListWnd->UserPort )) {

		CopyMem(( char * )m, ( char * )&MultiListMsg, (long)sizeof( struct IntuiMessage ));

		GT_ReplyIMsg( m );

		switch ( MultiListMsg.Class ) {

			case	IDCMP_REFRESHWINDOW:
				GT_BeginRefresh( MultiListWnd );
				GT_EndRefresh( MultiListWnd, TRUE );
				break;

			case	IDCMP_CLOSEWINDOW:
				running = MultiListCloseWindow();
				break;

			case	IDCMP_GADGETUP:
			case	IDCMP_GADGETDOWN:
				func = ( FPTR * )(( struct Gadget * )MultiListMsg.IAddress )->UserData;
				running = func();
				break;

			case	IDCMP_VANILLAKEY:
				running = MultiListVanillaKey();
				break;
		}
	}
	return( running );
}

int OpenMultiListWindow( void )
{
	struct NewGadget	ng;
	struct Gadget	*g;
	UWORD		lc, tc;
	UWORD		wleft = MultiListLeft, wtop = MultiListTop, ww, wh;

	ComputeFont( MultiListWidth, MultiListHeight );

	ww = ComputeX( MultiListWidth );
	wh = ComputeY( MultiListHeight );

	if (( wleft + ww + OffX + Scr->WBorRight ) > Scr->Width ) wleft = Scr->Width - ww;
	if (( wtop + wh + OffY + Scr->WBorBottom ) > Scr->Height ) wtop = Scr->Height - wh;

	if ( ! ( g = CreateContext( &MultiListGList )))
		return( 1L );

	for( lc = 0, tc = 0; lc < MultiList_CNT; lc++ ) {

		CopyMem((char * )&MultiListNGad[ lc ], (char * )&ng, (long)sizeof( struct NewGadget ));

		ng.ng_VisualInfo = VisualInfo;
		ng.ng_TextAttr   = Font;
		ng.ng_LeftEdge   = OffX + ComputeX( ng.ng_LeftEdge );
		ng.ng_TopEdge    = OffY + ComputeY( ng.ng_TopEdge );
		ng.ng_Width      = ComputeX( ng.ng_Width );
		ng.ng_Height     = ComputeY( ng.ng_Height);

		MultiListGadgets[ lc ] = g = CreateGadgetA((ULONG)MultiListGTypes[ lc ], g, &ng, ( struct TagItem * )&MultiListGTags[ tc ] );

		while( MultiListGTags[ tc ] ) tc += 2;
		tc++;

		if ( NOT g )
			return( 2L );
	}

	HookList.h_Entry = (ULONG (*)())HookEntryList;
	HookList.h_Data	 = NULL;

	if ( ! ( MultiListWnd = OpenWindowTags( NULL,
				WA_Left,	wleft,
				WA_Top,		wtop,
				WA_Width,	ww + OffX + Scr->WBorRight,
				WA_Height,	wh + OffY + Scr->WBorBottom,
				WA_IDCMP,	LISTVIEWIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|IDCMP_VANILLAKEY,
				WA_Flags,	WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_SMART_REFRESH,
				WA_Gadgets,	MultiListGList,
				WA_Title,	MultiListWdt,
				WA_ScreenTitle,	"Multi-select example",
				WA_PubScreen,	Scr,
				WA_Activate, TRUE,
				TAG_DONE )))
	return( 4L );

	GT_RefreshWindow( MultiListWnd, NULL );

	return( 0L );
}

void CloseMultiListWindow( void )
{
	if ( MultiListWnd        ) {
		CloseWindow( MultiListWnd );
		MultiListWnd = NULL;
	}

	if ( MultiListGList      ) {
		FreeGadgets( MultiListGList );
		MultiListGList = NULL;
	}
}


struct Requester Requester;

void DisableWindow(void) {
	InitRequester(&Requester);
	Request(&Requester,MultiListWnd);
	SetWindowPointer(MultiListWnd, WA_BusyPointer, TRUE,
						  WA_PointerDelay, TRUE,
						  TAG_END);
}

void EnableWindow(void) {
	EndRequest(&Requester,MultiListWnd);
	SetWindowPointer(MultiListWnd,
						  TAG_END);
}

int MListClicked( void )
{
	/* routine when gadget "_Multi Select List" is clicked. */
  DisableWindow();  // This disables the whole window
  GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
                    GTLV_Labels,~0,
                    TAG_END);
  MList0Nodes[CurEntry].Current = FALSE;
  CurEntry = MultiListMsg.Code;
  MList0Nodes[CurEntry].Current = TRUE;
  if (MList0Nodes[MultiListMsg.Code].Selected) {
    MList0Nodes[MultiListMsg.Code].Selected = FALSE;
  }
  else {
    MList0Nodes[MultiListMsg.Code].Selected = TRUE;
  }
  GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
                    GTLV_Labels,(ULONG)&MList0List,
                    TAG_END);
  EnableWindow();  // This enables the whole window
  return 1;
}

int MultiListCloseWindow( void )
{
	/* routine for "IDCMP_CLOSEWINDOW". */
	return 0;
}

int MultiListVanillaKey( void )
{
	/* routine for "IDCMP_VANILLAKEY". */
	switch (MultiListMsg.Code) {
	case 'm':
		DisableWindow();
	   GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
                   GTLV_Labels,~0,
                   TAG_END);
		MList0Nodes[CurEntry].Current = FALSE;
		++CurEntry;
		if (ENTRYCOUNT == CurEntry) {
			CurEntry = 0;
		}
		MList0Nodes[CurEntry].Current = TRUE;
		GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
	                     GTLV_Labels,(ULONG)&MList0List,
								GTLV_Selected, CurEntry,
								GTLV_MakeVisible, CurEntry,
								TAG_END);
		EnableWindow();
		break;
	case 'M':
		DisableWindow();
	   GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
                   GTLV_Labels,~0,
                   TAG_END);
		MList0Nodes[CurEntry].Current = FALSE;
		if (CurEntry) {
			--CurEntry;
		}
		else {
			CurEntry = ENTRYCOUNT-1;
		}
		MList0Nodes[CurEntry].Current = TRUE;
		GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
	                     GTLV_Labels,(ULONG)&MList0List,
								GTLV_Selected, CurEntry,
								GTLV_MakeVisible, CurEntry,
								TAG_END);
		EnableWindow();
		break;
	case ' ':
		DisableWindow();
	   GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
                   GTLV_Labels,~0,
                   TAG_END);
		if (MList0Nodes[CurEntry].Selected) {
			MList0Nodes[CurEntry].Selected = FALSE;
		}
		else {
			MList0Nodes[CurEntry].Selected = TRUE;
		}
		GT_SetGadgetAttrs(MultiListGadgets[GDX_MList],MultiListWnd,NULL,
		                  GTLV_Labels,(ULONG)&MList0List,
								GTLV_Selected, CurEntry,
								GTLV_MakeVisible, CurEntry,
								TAG_END);
		EnableWindow();
	}
	return 1;
}

int main(void) {
	int notdone = 1;
	if (!SetupScreen()) {
		if (!OpenMultiListWindow()) {
			while (notdone) {
				WaitPort(MultiListWnd->UserPort);
				notdone = HandleMultiListIDCMP();
			}
			CloseMultiListWindow();
		}
		CloseDownScreen();
	}
	return 0;
}
