/* BNKDEMO
 * An example for an BANKEDITOR xapp application
 * This is an editor for the Roland MT32
 * Author : Dominique Lorre
 * $Id$
 */

#include "bnkdemo.h"

VOID main(VOID)
{
int retval ;

    if (InitAll()) {
        ProcessEvents() ;
        retval =  CloseAll(0)?RETURN_OK:RETURN_FAIL;
    }
    else
        retval = RETURN_FAIL ;
    exit(retval) ;
}

/****** bnkdemo.c/InitAll **************************************************
*
*   NAME
*   InitAll -- Main Initializations
*
*   SYNOPSIS
*   success = InitAll()
*   BOOL InitAll(void)
*
*   FUNCTION
*       InitAll allocates all needed resources step by step and is responsible
*       for the initialization of the communication with Euterpe. CloseAll will
*       be called in case of failure.
*
*   INPUTS
*       None
*
*   RESULTS
*       success     TRUE if the initialization succeed, FALSE if not
*
*   BUGS
*       This function has a weak point because it may fail if Euterpe is closing
*       when this task is loading. Forbid()/Permit() might solve the problem.
*       Anyway, under normal use effects will not be loaded when Euterpe is
*       exiting and developers have been asked not to use Forbid(). If
*       someone has an idea...
*
*   SEE ALSO
*       fxdemo.c/CloseAll
****************************************************************************
*
*/
BOOL InitAll(void)
{
WORD i, j, k ;
    // First, create a ReplyPort for communication with Euterpe

    replyPort = CreateMsgPort() ;
    if (!replyPort)
        return CloseAll(1) ;

    // Now, initialize the Message

    if (xmsg = (struct XAppMsg *)AllocVec(sizeof(struct XAppMsg), MEMF_CLEAR|MEMF_PUBLIC)) {
        xmsg->xm_Message.mn_Node.ln_Type = NT_MESSAGE ;
        xmsg->xm_Message.mn_Length = sizeof( struct XAppMsg ) ;
        xmsg->xm_Message.mn_ReplyPort = replyPort ;
    }
    else
        return CloseAll(2) ;


    // Parameters initialization
    bData.bd_UnitNumber = 16 ;
    bData.bd_ToneNumber = 0 ;
    bData.bd_ProgramNumber = 0 ;
    bData.bd_PartNumber = 0 ;
    bData.bd_PartialNumber = 0 ;
    bData.bd_ParamNumber = 0 ;

    for (i=0; i<64; i++) {
        strcpy(bData.bd_Tone[i].to_Name, "UnNamed") ;
        bData.bd_Tone[i].to_P12 = 0 ;
        bData.bd_Tone[i].to_P34 = 0 ;
        bData.bd_Tone[i].to_PMute= 0 ;
        bData.bd_Tone[i].to_EnvMode = 0 ;
        for (j=0; j<4; j++) {
            for (k=0; k<58; k++)
                bData.bd_Tone[i].to_Partial[j][k] = 0 ;
        }
    }

    // Look at Euterpe's xapp port

    if (XAppPort = FindPort((UBYTE *)XAPPPORTNAME)) {
        // Euterpe is here, let's register
        xmsg->xm_Action = XAPPACT_ADD ;
        xmsg->xm_Tags = ti ;
        PutMsg(XAppPort, (struct Message *)xmsg);
        WaitPort(replyPort) ;
        replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
        // Euterpe has given us a name for our port
        xHandle = replymsg->xm_Result ;
        if (!xHandle)
            return CloseAll(3) ;
        th[0].ti_Data = td[0].ti_Data = rd[0].ti_Data = (ULONG)xHandle ;
        td[1].ti_Data = rd[1].ti_Data = (ULONG)midibuffer ;
    }
    else
        return CloseAll(3) ;

    // Now, create our Port with the name given by Euterpe

    MyPort = CreatePort(MyPortName, NULL);

    // We are ready now, tell Euterpe how the init was

    xmsg->xm_Action = MyPort ? XAPPACT_INIT : XAPPACT_FAILURE ;
    xmsg->xm_Tags = th ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;

    if (!MyPort)
        return CloseAll(4) ;

     msig = 1 << MyPort->mp_SigBit ;

     // A bank editor should open its window on startup
     if (InitWin())
        winopen = TRUE ;

     return TRUE ;
}
/****** bnkdemo.c/CloseAll *************************************************
*
*   NAME
*   CloseAll -- Deallocates the resources
*
*   SYNOPSIS
*   success = CloseAll(level)
*   BOOL CloseAll(WORD)
*
*   FUNCTION
*       CloseAll() will deallocate all the successfully allocated resources
*
*   INPUTS
*       level   0 if normal deallocation or the step where allocation failed
*
*   RESULTS
*       success TRUE if called with level=0 FALSE in other cases
*
****************************************************************************
*
*/

BOOL CloseAll(WORD level)
{
    switch(level) {
        case 0:
            DeletePort(MyPort) ;
        case 4:
        case 3:
            FreeVec(xmsg) ;
        case 2:
            DeleteMsgPort(replyPort) ;
    }
    return (BOOL) ( level ? FALSE : TRUE ) ;
}

void ProcessEvents(void)
{
ULONG           sigmask ;
BOOL            end ;
enum XAppAction action ;

    end = FALSE ;
    winopen = TRUE ;

    while (!end) {
        sigmask = msig ;
        if (winopen) sigmask |= winsig ;
        signal = Wait(sigmask) ;
        if (signal & winsig) {
            // A better way of doing this could be to place the semaphore
            // calls at the beginning of each modification of the bData fields
            ProcessWinEvents() ;
            if (!winopen)
                CloseWin(0) ;
        }
        if (signal & msig) {
            while (emsg = (struct XAppMsg *)GetMsg(MyPort)) {
                action = emsg->xm_Action ;
                switch (action) {   // processing BEFORE ReplyMsg
                case XAPPACT_SHOW:
                case XAPPACT_HIDE:
                case XAPPACT_REMOVE:
                    emsg->xm_Error = XAPPERR_OK ;   // fine
                    break ;
                case XAPPACT_PROGRAMLIST:
                    GetProgramList() ;
                    emsg->xm_Error = XAPPERR_OK ;   // fine
                    break ;
                default:
                    emsg->xm_Error = XAPPERR_ACTION_NOT_KNOWN ;
                    break ;
                }
                // Do not reply to a remove msg
                // since Euterpe does not wait for it
                if (action != XAPPACT_REMOVE)
                    ReplyMsg((struct Message *)emsg) ;
                switch (action) {   // processing AFTER ReplyMsg
                    case XAPPACT_SHOW:
                        if (!winopen && InitWin())
                            winopen = TRUE ;
                        break ;
                    case XAPPACT_REMOVE:
                        end = TRUE ;
                    case XAPPACT_HIDE:
                        if (winopen) {
                            CloseWin(0) ;
                        }
                        break ;
                }
            }
        }
    }
}

LONG __saveds PartialParameter(struct Gadget *g,  WORD level)
{
    return (LONG)partial[level] ;
}

BOOL InitWin(void)
{
struct NewGadget ng ;
struct TextExtent te ;
UWORD winZoom[4] = { 50, 50, 300, 20 } ;
WORD    maxpartialname, i ;
char    *unstr = "Unit : %ld" ;
char    *prgstr = "Program : %ld" ;
char    *tostr = "Tone : %ld" ;
char    *partstr = "Part : %ld" ;
char    *partialstr = "Partial : %ld" ;
char    *paramstr = "%21s" ;
char    *valstr = "Value : %ld" ;
char    *sp1str = "Partials 1 & 2 : %ld" ;
char    *sp2str = "Partials 3 & 4 : %ld" ;



    ps = LockPubScreen((UBYTE *)"Euterpe") ; // Get the Euterpe public screen
    if (!ps)
        return CloseWin(1) ;

    maxpartialname = 0 ;
    for (i=0; i<58; i++) {
        TextExtent(&ps->RastPort, partial[i],
            strlen(partial[i]), &te) ;
        maxpartialname = MAX(maxpartialname, te.te_Width) ;

    }
    winZoom[0] = winZoom[3] = ps->BarHeight ;  // cosmetic details

    vi = GetVisualInfo(ps, NULL) ;
    if (!vi)
        return CloseWin(2) ;

    glist = NULL ;
    gad = CreateContext(&glist) ;
    if (!gad)
        return CloseWin(3) ;

    ng.ng_VisualInfo = vi ;
    ng.ng_TextAttr = ps->Font ;

    ng.ng_GadgetText = (UBYTE *)"Upload" ;
    TextExtent(&ps->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText), &te) ;
    ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = te.te_Width + 8 ;
    ng.ng_Height = ng.ng_TextAttr->ta_YSize * 3 / 2 ;
    ng.ng_GadgetID = UPLOAD_ID ;
    ng.ng_Flags = PLACETEXT_IN ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(BUTTON_KIND, gad, &ng,
        TAG_DONE);

    if (!gad)
        return CloseWin(4);


    ng.ng_GadgetText = (UBYTE *)"DownLoad" ;
    TextExtent(&ps->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText), &te) ;
    ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
    ng.ng_LeftEdge =  ng.ng_LeftEdge + ng.ng_Width + 8 ;
    ng.ng_Width = te.te_Width + 8 ;
    ng.ng_GadgetID = DOWNLOAD_ID ;
    ng.ng_Flags = PLACETEXT_IN ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(BUTTON_KIND, gad, &ng,
        TAG_DONE);

    if (!gad)
        return CloseWin(4);

    ng.ng_GadgetText = (UBYTE *)"Load" ;
    TextExtent(&ps->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText), &te) ;
    ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
    ng.ng_LeftEdge =  ng.ng_LeftEdge + ng.ng_Width + 8 ;
    ng.ng_Width = te.te_Width + 8 ;
    ng.ng_GadgetID = LOAD_ID ;
    ng.ng_Flags = PLACETEXT_IN ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(BUTTON_KIND, gad, &ng,
        TAG_DONE);

    if (!gad)
        return CloseWin(4);

    ng.ng_GadgetText = (UBYTE *)"Save" ;
    TextExtent(&ps->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText), &te) ;
    ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
    ng.ng_LeftEdge =  ng.ng_LeftEdge + ng.ng_Width + 8 ;
    ng.ng_Width = te.te_Width + 8 ;
    ng.ng_GadgetID = SAVE_ID ;
    ng.ng_Flags = PLACETEXT_IN ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(BUTTON_KIND, gad, &ng,
        TAG_DONE);

    if (!gad)
        return CloseWin(4);

    ng.ng_GadgetText = (UBYTE *)"Set Part" ;
    TextExtent(&ps->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText), &te) ;
    ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
    ng.ng_LeftEdge =  ng.ng_LeftEdge + ng.ng_Width + 8 ;
    ng.ng_Width = te.te_Width + 8 ;
    ng.ng_GadgetID = SETPART_ID ;
    ng.ng_Flags = PLACETEXT_IN ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(BUTTON_KIND, gad, &ng,
        TAG_DONE);

    if (!gad)
        return CloseWin(4);

    ng.ng_GadgetText = (UBYTE *)"Set Program" ;
    TextExtent(&ps->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText), &te) ;
    ng.ng_TopEdge = ps->BarHeight + 8 ; // font adaptative method
    ng.ng_LeftEdge =  ng.ng_LeftEdge + ng.ng_Width + 8 ;
    ng.ng_Width = te.te_Width + 8 ;
    ng.ng_GadgetID = SETPRG_ID ;
    ng.ng_Flags = PLACETEXT_IN ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(BUTTON_KIND, gad, &ng,
        TAG_DONE);

    if (!gad)
        return CloseWin(4);

    ng.ng_LeftEdge =  ng.ng_LeftEdge + ng.ng_Width + 8 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = UNIT_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = NULL ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           1,
    GTSL_Max,           32,
    GTSL_Level,         bData.bd_UnitNumber+1,
    GTSL_LevelFormat,   unstr,
    GTSL_MaxLevelLen,   20,
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_TopEdge += ng.ng_Height + 8 ;
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = PROGRAM_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = NULL ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           1,
    GTSL_Max,           128,
    GTSL_Level,         bData.bd_ProgramNumber+1,
    GTSL_LevelFormat,   prgstr,
    GTSL_MaxLevelLen,   20,
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_LeftEdge =  300 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = PART_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           1,
    GTSL_Max,           8,
    GTSL_Level,         bData.bd_PartNumber+1,
    GTSL_LevelFormat,   partstr,
    GTSL_MaxLevelLen,   20,
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_TopEdge += ng.ng_Height + 8 ;
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = TONE_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;

    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           1,
    GTSL_Max,           64,
    GTSL_Level,         bData.bd_ToneNumber+1,
    GTSL_LevelFormat,   tostr,
    GTSL_MaxLevelLen,   20,
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_LeftEdge =  300 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = TONENAME_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    strncpy(tonename, bData.bd_Tone[bData.bd_ToneNumber].to_Name, 10) ;
    tonename[10] = '\0' ;


    ng.ng_GadgetText = (UBYTE *)"Tone Name" ;
    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(STRING_KIND, gad, &ng,
    GTST_String,        tonename,
    GTST_MaxChars,      10,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_TopEdge += ng.ng_Height + 8 ;
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = SPARTIAL1_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = NULL ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           0,
    GTSL_Max,           12,
    GTSL_Level,         bData.bd_Tone[bData.bd_ToneNumber].to_P12,
    GTSL_LevelFormat,   sp1str,
    GTSL_MaxLevelLen,   strlen(sp1str),
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_LeftEdge =  300 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = SPARTIAL2_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           0,
    GTSL_Max,           12,
    GTSL_Level,         bData.bd_Tone[bData.bd_ToneNumber].to_P34,
    GTSL_LevelFormat,   sp2str,
    GTSL_MaxLevelLen,   strlen(sp2str),
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_TopEdge += ng.ng_Height + 8 ;
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = ENVMODE_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = "ENV MODE (Sustain)" ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(CHECKBOX_KIND, gad, &ng,
    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_EnvMode,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);


    ng.ng_TopEdge += ng.ng_Height + 8 ;
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = PARTIAL_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = NULL ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           1,
    GTSL_Max,           4,
    GTSL_Level,         bData.bd_PartialNumber+1,
    GTSL_LevelFormat,   partialstr,
    GTSL_MaxLevelLen,   strlen(partialstr),
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    ng.ng_LeftEdge =  300 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = PARTIALMUTE_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = "Use Partial" ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(CHECKBOX_KIND, gad, &ng,
    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_PMute & (1 << bData.bd_PartialNumber),
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    TextExtent(&ps->RastPort, paramstr, strlen(paramstr), &te) ;
    ng.ng_TopEdge += ng.ng_Height + 8 ;
    ng.ng_LeftEdge =  20 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = PARAMETER_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;
    ng.ng_GadgetText = NULL ;

    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           0,
    GTSL_Max,           57,
    GTSL_Level,         bData.bd_ParamNumber,
    GTSL_LevelFormat,   paramstr,
    GTSL_DispFunc,      PartialParameter,
    GTSL_MaxLevelLen,   strlen(paramstr)+21,
    GTSL_MaxPixelLen,   maxpartialname+te.te_Width+8,
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);

    TextExtent(&ps->RastPort, valstr, strlen(valstr), &te) ;
    ng.ng_LeftEdge =  300 ;
    ng.ng_Width = 128 ;
    ng.ng_GadgetID = VALUE_ID ;
    ng.ng_Flags = PLACETEXT_RIGHT ;


    gad  = tabgad[ng.ng_GadgetID] = CreateGadget(SLIDER_KIND, gad, &ng,
    GTSL_Min,           minpartial[bData.bd_ParamNumber],
    GTSL_Max,           maxpartial[bData.bd_ParamNumber],
    GTSL_Level,         bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber],
    GTSL_LevelFormat,   valstr,
    GTSL_MaxLevelLen,   strlen(valstr),
    GTSL_LevelPlace,    PLACETEXT_RIGHT,
    GA_RelVerify,       TRUE,
    TAG_DONE);
    if (!gad)
        return CloseWin(4);


    /* Open a simple window */
    xappwin = OpenWindowTags(NULL,
    WA_Left,            20,
    WA_Top,             ps->BarHeight,
    WA_Width,           620,
    WA_Height,          ng.ng_TopEdge + ng.ng_Height + 8, // font adaptative
    WA_Title,           MyPortName,
    WA_CloseGadget,     TRUE,
    WA_DepthGadget,     TRUE,
    WA_Gadgets,         glist,
    WA_IDCMP,           IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|
                        CHECKBOXIDCMP|SLIDERIDCMP|BUTTONIDCMP|STRINGIDCMP,
    WA_DragBar,         TRUE,
    WA_Activate,        TRUE,
    WA_PubScreen,       ps,
    WA_Zoom,            winZoom,
    TAG_DONE) ;

    if (xappwin) {
        GT_RefreshWindow(xappwin, NULL) ;
        winsig = 1 << xappwin->UserPort->mp_SigBit ;
        return TRUE ;
    }
    else
        return CloseWin(5) ;

}

BOOL CloseWin(WORD level)
{
    winopen = FALSE ;
    switch(level) {
    case 0:
        StripWindow(xappwin) ;
        CloseWindow(xappwin) ;
        xappwin = NULL ;
        winsig = 0 ;
    case 5:
    case 4:
        FreeGadgets(glist) ;
    case 3:
        FreeVisualInfo(vi) ;
    case 2:
        UnlockPubScreen(NULL, ps) ;
    }
    return (BOOL) ( level ? FALSE : TRUE ) ;
}

void ProcessWinEvents(void)
{
ULONG                   classe ;
UWORD                   code ;
struct IntuiMessage*    imsg ;
struct Gadget*          gad ;

    while (imsg = GT_GetIMsg(xappwin->UserPort)) {
        classe = imsg->Class ;
        code = imsg->Code ;
        gad = (struct Gadget *)imsg->IAddress ;
        GT_ReplyIMsg(imsg) ;
        switch (classe) {
        case IDCMP_REFRESHWINDOW:
            GT_BeginRefresh(xappwin) ;
            GT_EndRefresh(xappwin, TRUE) ;
            break ;
        case IDCMP_GADGETUP:
            switch (gad->GadgetID) {
            case UPLOAD_ID:
                UploadTone() ;
                break ;
            case DOWNLOAD_ID:
                DownLoadTone() ;
                break ;
            case LOAD_ID:
                LoadTone() ;
                break ;
            case SAVE_ID:
                SaveTone() ;
                break ;
            case SETPART_ID:
                SetPart() ;
                break ;
            case SETPRG_ID:
                SetPrg() ;
                break ;
            case UNIT_ID:
                bData.bd_UnitNumber = code - 1 ;
                break ;
            case PROGRAM_ID:
                bData.bd_ProgramNumber = code - 1 ;
                break ;
            case PART_ID:
                bData.bd_PartNumber = code - 1 ;
                break ;
            case TONE_ID:
                bData.bd_ToneNumber = code - 1 ;
                strncpy(tonename, bData.bd_Tone[bData.bd_ToneNumber].to_Name, 10) ;
                tonename[10] = '\0' ;
                GT_SetGadgetAttrs(tabgad[TONENAME_ID], xappwin, NULL,
                    GTST_String, tonename,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[SPARTIAL1_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_P12+1,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[SPARTIAL2_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_P34+1,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[ENVMODE_ID], xappwin, NULL,
                    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_EnvMode,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[PARTIALMUTE_ID], xappwin, NULL,
                    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_PMute & (1 << bData.bd_PartialNumber),
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[VALUE_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber],
                    GTSL_Min,   minpartial[bData.bd_ParamNumber],
                    GTSL_Max,   maxpartial[bData.bd_ParamNumber],
                    TAG_DONE) ;
                break ;
            case TONENAME_ID:
                strncpy(bData.bd_Tone[bData.bd_ToneNumber].to_Name, ((struct StringInfo *)gad->SpecialInfo)->Buffer, 10) ;
                PatchName() ;
                break ;
            case SPARTIAL1_ID:
                bData.bd_Tone[bData.bd_ToneNumber].to_P12 = code - 1 ;
                break ;
            case SPARTIAL2_ID:
                bData.bd_Tone[bData.bd_ToneNumber].to_P34 = code - 1 ;
                break ;
            case ENVMODE_ID:
                bData.bd_Tone[bData.bd_ToneNumber].to_EnvMode = code ? 1 : 0 ;
                break ;
            case PARTIALMUTE_ID:
                if (code)
                    bData.bd_Tone[bData.bd_ToneNumber].to_PMute |= 1 << bData.bd_PartialNumber ;
                else
                    bData.bd_Tone[bData.bd_ToneNumber].to_PMute &= ~(1 << bData.bd_PartialNumber) ;
                break ;
            case PARTIAL_ID:
                bData.bd_PartialNumber = code - 1 ;
                GT_SetGadgetAttrs(tabgad[PARTIALMUTE_ID], xappwin, NULL,
                    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_PMute & (1 << bData.bd_PartialNumber),
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[VALUE_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber],
                    GTSL_Min,   minpartial[bData.bd_ParamNumber],
                    GTSL_Max,   maxpartial[bData.bd_ParamNumber],
                    TAG_DONE) ;
                break ;
            case PARAMETER_ID:
                bData.bd_ParamNumber = code ;
                GT_SetGadgetAttrs(tabgad[VALUE_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber],
                    GTSL_Min,   minpartial[bData.bd_ParamNumber],
                    GTSL_Max,   maxpartial[bData.bd_ParamNumber],
                    TAG_DONE) ;
                break ;
            case VALUE_ID:
                bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber] = code ;
                break ;
            }
            break ;
        case IDCMP_CLOSEWINDOW:
            winopen = FALSE ;
            break ;
        }
    }
}

/* Useful C= functions */

VOID StripWindow(struct Window *win)
{
    Forbid();
    StripIntuiMessages(win->UserPort, win);
    win->UserPort = NULL;
    ModifyIDCMP(win, NULL);
    Permit();
}

VOID    StripIntuiMessages( struct MsgPort *mp, struct Window *win )
        {
            struct IntuiMessage *msg;
            struct Node *succ;

            msg = (struct IntuiMessage *) mp->mp_MsgList.lh_Head;

            while( succ =  msg->ExecMessage.mn_Node.ln_Succ ) {

                if( msg->IDCMPWindow ==  win ) {

                    /* Intuition is about to free this message.
                     * Make sure that we have politely sent it back.
                     */
                    Remove( (struct Node *)msg );

                    ReplyMsg( (struct Message *)msg );
                }

                msg = (struct IntuiMessage *) succ;
            }
        }

/* Another one for a private MsgPort */

VOID    StripMessages( struct MsgPort *mp )
        {
            struct Message *msg;
            struct Node *succ;

            msg = (struct Message *) mp->mp_MsgList.lh_Head;

            while( succ =  msg->mn_Node.ln_Succ ) {

                    Remove( (struct Node *)msg );

                    ReplyMsg( msg );

                msg = (struct Message *) succ;
            }
        }



void UploadTone(void)
{
ULONG   chk ;
LONG    i ;

    td[2].ti_Data = 266 ;

    midibuffer[0] = 0xF0 ;
    midibuffer[1] = 0x41 ;
    midibuffer[2] = bData.bd_UnitNumber ;
    midibuffer[3] = 0x16 ;
    midibuffer[4] = 0x12 ;
    midibuffer[5] = 0x08 ;
    midibuffer[6] = bData.bd_ToneNumber * 2 ;
    midibuffer[7] = 0x00 ;
    CopyMem(&bData.bd_Tone[bData.bd_ToneNumber], midibuffer+8, 246) ;
    memset(midibuffer+254, 0, 10) ;
    for (i=5, chk = 0; i<254; i++) {
        chk +=midibuffer[i] ;
    }
    midibuffer[264] = 0x80 - (chk & 0x7F) ;
    midibuffer[265]  = 0xF7 ;

    xmsg->xm_Action = XAPPACT_SENDMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
}

void DownLoadTone(void)
{
WORD i, timeout, rlen ;
ULONG chk ;

    td[2].ti_Data = 13 ;
    rd[2].ti_Data = 266 ;

    midibuffer[0] = 0xF0 ;
    midibuffer[1] = 0x41 ;
    midibuffer[2] = bData.bd_UnitNumber ;
    midibuffer[3] = 0x16 ;
    midibuffer[4] = 0x11 ;
    midibuffer[5] = 0x08 ;
    midibuffer[6] = bData.bd_ToneNumber * 2 ;
    midibuffer[7] = 0x00 ;
    midibuffer[8] = 0x00 ;
    midibuffer[9] = 0x02 ;
    midibuffer[10] = 0x00 ;
    for (i=5, chk = 0; i<11; i++) {
        chk +=midibuffer[i] ;
    }
    midibuffer[11] = 0x80 - (chk & 0x7F) ;
    midibuffer[12]  = 0xF7 ;

    xmsg->xm_Action = XAPPACT_LOCKMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;

    xmsg->xm_Action = XAPPACT_SENDMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;

    memset(midibuffer, 0, 266) ;

    timeout = rlen = 0 ;

    do {
        xmsg->xm_Action = XAPPACT_RECVMIDI ;
        xmsg->xm_Tags = rd ;
        PutMsg(XAppPort, (struct Message *)xmsg);
        WaitPort(replyPort) ;
        replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
        if (!replymsg->xm_Length) {
            timeout++ ;
            Delay(10) ;
        }
        else {
            rlen += replymsg->xm_Length ;
            if (rlen < 266) {
                rd[1].ti_Data = (ULONG)&midibuffer[rlen] ;
                rd[2].ti_Data = (ULONG)266-rlen ;
            }
        }

    } while (timeout < 3 && rlen < 266) ;

    xmsg->xm_Action = XAPPACT_UNLOCKMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;

    if (rlen == 266) {
        CopyMem(midibuffer+8, &bData.bd_Tone[bData.bd_ToneNumber], 246) ;
        strncpy(tonename, bData.bd_Tone[bData.bd_ToneNumber].to_Name, 10) ;
        tonename[10] = '\0' ;
        GT_SetGadgetAttrs(tabgad[TONENAME_ID], xappwin, NULL,
            GTST_String, tonename,
            TAG_DONE) ;
        GT_SetGadgetAttrs(tabgad[SPARTIAL1_ID], xappwin, NULL,
            GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_P12+1,
            TAG_DONE) ;
        GT_SetGadgetAttrs(tabgad[SPARTIAL2_ID], xappwin, NULL,
            GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_P34+1,
            TAG_DONE) ;
        GT_SetGadgetAttrs(tabgad[ENVMODE_ID], xappwin, NULL,
            GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_EnvMode,
            TAG_DONE) ;
        GT_SetGadgetAttrs(tabgad[PARTIALMUTE_ID], xappwin, NULL,
            GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_PMute & (1 << bData.bd_PartialNumber),
            TAG_DONE) ;
        GT_SetGadgetAttrs(tabgad[VALUE_ID], xappwin, NULL,
            GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber],
            GTSL_Min,   minpartial[bData.bd_ParamNumber],
            GTSL_Max,   maxpartial[bData.bd_ParamNumber],
            TAG_DONE) ;
    }
    rd[1].ti_Data = (ULONG)midibuffer ; // Reset the tag on exit
}

void LoadTone(void)
{
BPTR fh ;
LONG n ;

    if (getfile(ps, fname, pname, "#?", "Load a Tone", FALSE)) {
            strcpy(filename, pname) ;
            AddPart(filename, fname, 80) ;
            if (fh = Open(filename, MODE_OLDFILE)) {
                n = Read(fh, &bData.bd_Tone[bData.bd_ToneNumber], 246) ;
                Close(fh) ;
            }
            if (n == 246) {
                PatchName() ;
                strncpy(tonename, bData.bd_Tone[bData.bd_ToneNumber].to_Name, 10) ;
                tonename[10] = '\0' ;
                GT_SetGadgetAttrs(tabgad[TONENAME_ID], xappwin, NULL,
                    GTST_String, tonename,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[SPARTIAL1_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_P12+1,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[SPARTIAL2_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_P34+1,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[ENVMODE_ID], xappwin, NULL,
                    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_EnvMode,
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[PARTIALMUTE_ID], xappwin, NULL,
                    GTCB_Checked, bData.bd_Tone[bData.bd_ToneNumber].to_PMute & (1 << bData.bd_PartialNumber),
                    TAG_DONE) ;
                GT_SetGadgetAttrs(tabgad[VALUE_ID], xappwin, NULL,
                    GTSL_Level, bData.bd_Tone[bData.bd_ToneNumber].to_Partial[bData.bd_PartialNumber][bData.bd_ParamNumber],
                    GTSL_Min,   minpartial[bData.bd_ParamNumber],
                    GTSL_Max,   maxpartial[bData.bd_ParamNumber],
                    TAG_DONE) ;
            }
    }
}

void SaveTone(void)
{
BPTR fh ;

    strcpy(fname, tonename) ;
    if (getfile(ps, fname, pname, "#?", "Save a Tone", TRUE)) {
            strcpy(filename, pname) ;
            AddPart(filename, fname, 80) ;
            if (fh = Open(filename, MODE_NEWFILE)) {
                Write(fh, &bData.bd_Tone[bData.bd_ToneNumber], 246) ;
                Close(fh) ;
            }
    }
}

void SetPart(void)
{
ULONG   chk ;
WORD    i ;

    UploadTone() ;

    td[2].ti_Data = 12 ;
    midibuffer[0] = 0xF0 ;
    midibuffer[1] = 0x41 ;
    midibuffer[2] = bData.bd_UnitNumber ;
    midibuffer[3] = 0x16 ;
    midibuffer[4] = 0x12 ;
    midibuffer[5] = 0x03 ;
    midibuffer[6] = 0x00 ;
    midibuffer[7] = 0x10 * bData.bd_PartNumber ;
    midibuffer[8] = 0x02 ;
    midibuffer[9] = bData.bd_ToneNumber ;
    for (i=5, chk = 0; i<10; i++) {
        chk +=midibuffer[i] ;
    }
    midibuffer[10] = 0x80 - (chk & 0x7F) ;
    midibuffer[11]  = 0xF7 ;

    xmsg->xm_Action = XAPPACT_SENDMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
}

void SetPrg(void)
{
ULONG   chk ;
WORD    i ;

    UploadTone() ;

    td[2].ti_Data = 12 ;
    midibuffer[0] = 0xF0 ;
    midibuffer[1] = 0x41 ;
    midibuffer[2] = bData.bd_UnitNumber ;
    midibuffer[3] = 0x16 ;
    midibuffer[4] = 0x12 ;
    midibuffer[5] = 0x05 ;
    midibuffer[6] = 0x00 ;
    midibuffer[7] = 0x10 * bData.bd_ProgramNumber ;
    midibuffer[8] = 0x02 ;
    midibuffer[9] = bData.bd_ToneNumber ;
    for (i=5, chk = 0; i<10; i++) {
        chk +=midibuffer[i] ;
    }
    midibuffer[10] = 0x80 - (chk & 0x7F) ;
    midibuffer[11]  = 0xF7 ;

    xmsg->xm_Action = XAPPACT_SENDMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
}

void GetProgramList(void)
{
ULONG   chk ;
WORD    i ;

    UploadTone() ;

    td[2].ti_Data = 12 ;
    midibuffer[0] = 0xF0 ;
    midibuffer[1] = 0x41 ;
    midibuffer[2] = bData.bd_UnitNumber ;
    midibuffer[3] = 0x16 ;
    midibuffer[4] = 0x12 ;
    midibuffer[5] = 0x05 ;
    midibuffer[6] = 0x00 ;
    midibuffer[7] = 0x10 * bData.bd_ProgramNumber ;
    midibuffer[8] = 0x02 ;
    midibuffer[9] = bData.bd_ToneNumber ;
    for (i=5, chk = 0; i<10; i++) {
        chk +=midibuffer[i] ;
    }
    midibuffer[10] = 0x80 - (chk & 0x7F) ;
    midibuffer[11]  = 0xF7 ;

    xmsg->xm_Action = XAPPACT_SENDMIDI ;
    xmsg->xm_Tags = td ;
    PutMsg(XAppPort, (struct Message *)xmsg);
    WaitPort(replyPort) ;
    replymsg = (struct XAppMsg *)GetMsg(replyPort) ;
}


struct FileRequester *InitAslFileReq(struct Screen *s, STRPTR drawer, const char *title)
{
struct FileRequester *fr = NULL ;

    fr = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
        ASLFR_Screen,           s,
        ASLFR_RejectIcons,      TRUE,
        ASLFR_TitleText,        title,
        ASLFR_InitialDrawer,    drawer,
        TAG_DONE) ;
    return fr ;
}



BOOL getfile(struct Screen *s, STRPTR name, STRPTR drawer, STRPTR pat, const char *title, BOOL savemode)
{
struct FileRequester *fr ;
BOOL retval = FALSE ;

    if (fr = InitAslFileReq(s, drawer, title)) {
        if (AslRequestTags(fr,
            ASLFR_InitialFile,      name,
            ASLFR_InitialPattern,   pat,
            ASLFR_DoSaveMode,       savemode,
            ASLFR_DoPatterns,       TRUE,
            TAG_DONE)) {
            strcpy(name, fr->fr_File) ;
            strcpy(drawer, fr->fr_Drawer) ;
            retval = TRUE ;
        }
        FreeAslRequest(fr) ;
    }
    return retval ;
}

void PatchName(void)
{
LONG n ;
UBYTE c ;

    n = 0 ;
    do {
        c = bData.bd_Tone[bData.bd_ToneNumber].to_Name[n] ;
        if ((c < 32) || (c > 127 ))
            bData.bd_Tone[bData.bd_ToneNumber].to_Name[n] = ' ' ;
        n++ ;
    } while (c && (n<10)) ;
    while(n<10) {
        bData.bd_Tone[bData.bd_ToneNumber].to_Name[n++] = ' ' ;
    }
}
