/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include <intuition/intuition.h>
#include <devices/input.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfxmacros.h>
#include "tools.h"
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/layers.h>
#include <string.h>
#include <math.h>
#include "edit.h"
#include "notate.h"
#include "all.h"
#include "noteimage.h"

extern struct Functions functions;
static char doinglisten = 0;
char sendalltypes = 0;

/*======================================================================*/
/*   Place pointer at specific place in window                          */
/*======================================================================*/
void MovePointer( w, x, y)

register struct Window *w;
register short x, y;

{
register struct IOStdReq *ioreq;
struct InputEvent         mouse;
register struct Screen   *screen  =  w->WScreen;
register struct MsgPort  *ioport;

        x = x - w->MouseX ;
        if( !(screen->ViewPort.Modes & HIRES))  x = x << 1;

        y = y - w->MouseY ;
        if( !(screen->ViewPort.Modes & LACE))   y = y << 1;

        ioport = (struct MsgPort *) CreatePort ((char *)0,0L) ;
        if (ioport) {
                ioreq = (struct IOStdReq *) CreateStdIO(ioport);
                if (ioreq) {
                        OpenDevice("input.device",0L,(struct IORequest *)ioreq,0L);
                        ioreq->io_Command = IND_WRITEEVENT;
                        ioreq->io_Flags = 0;
                        ioreq->io_Length = sizeof (struct InputEvent);
                        ioreq->io_Data = (APTR) &mouse;
                        mouse.ie_NextEvent = NULL;
                        mouse.ie_Class = IECLASS_RAWMOUSE;
                        mouse.ie_TimeStamp.tv_secs = 0;
                        mouse.ie_TimeStamp.tv_micro = 0;
                        mouse.ie_Code = IECODE_NOBUTTON;
                        mouse.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE ;
                        mouse.ie_X = x;
                        mouse.ie_Y = y;
                        DoIO((struct IORequest *)ioreq);
                        CloseDevice((struct IORequest *)ioreq);
                        DeleteStdIO(ioreq);
                }
                DeletePort (ioport);
        }
}       /* end of MovePointer() */

static void sendtotool(struct Tool *tool,struct NoteEvent *note,long time)

{
    struct NoteEvent *copy;
    if (sendalltypes ||
            (note->status == MIDI_NOTEON) || (note->status == MIDI_PCHANGE)) {
        copy = (struct NoteEvent *) allocevent();
        if (copy) {
            copy->status = note->status;
            copy->value = note->value;
            copy->velocity = note->velocity;
            copy->time = time;
            copy->tool = tool;
            qevent((struct Event *)copy);
        }
        if (note->status == MIDI_NOTEON) {
            copy = (struct NoteEvent *) allocevent();
            if (copy) {
                copy->status = MIDI_NOTEOFF;
                copy->value = note->value;
                copy->velocity = note->velocity;
                if ((note->duration > 768) && !doinglisten) {
                    copy->time = time + 768;
                }
                else copy->time = time + note->duration;
                copy->tool = tool;
                qevent((struct Event *)copy);
            }
        }
    }
}

void sendonenoteout(edit,note,time)

struct Edit *edit;
struct NoteEvent *note;
long time;

{
    if (!note || !edit->track || (note->status == MIDI_NOTEOFF)) return;
    if ((edit->modebits & MB_PLAYNOTES) && (note->type == EVENT_VOICE)) {
        sendtotool(edit->track->tool.next,note,time);
    }
}

void sendnoteout(edit,note)

struct Edit *edit;
struct NoteEvent *note;

{
    sendonenoteout(edit,note,functions.timenow);
    if (!edit->listedit) displayedittime(edit,note->time);
}

void sendchordout(edit,chord)

struct Edit *edit;
struct ChordEvent *chord;

{
    struct NotateEvent note;
    short i;
    long bit;
    if (chord) {
        for (i=0;i<24;i++) {
            bit = 1 << i;
            if (bit & chord->chord->pattern) {
                note.status = MIDI_NOTEON;
                note.type = EVENT_VOICE;
                note.duration = 384;
                note.value = chord->root + i + 60;
                note.velocity = 70;
                sendonenoteout((struct Edit *)edit,(struct NoteEvent *)&note,
                                                    functions.timenow);
            }
        }
    }
}

void sendanyeventout(edit,note)

struct Edit *edit;
struct NoteEvent *note;

{
    char oldvalue;
    sendalltypes = 1;
    sendonenoteout(edit,note,functions.timenow);
    if (note->status == MIDI_PCHANGE) {
        oldvalue = note->value;
        note->value = 64;
        note->duration = 144;
        note->status = MIDI_NOTEON;
        note->velocity = 64;
        sendonenoteout(edit,note,functions.timenow+2);
        note->status = MIDI_PCHANGE;
        note->value = oldvalue;
    }
    sendalltypes = 0;
    if (!edit->listedit) displayedittime(edit,note->time);
}

void sendclipnotes(struct Edit *edit,long begin, long end)
{
  if (functions.running) return;
  setselectedtrack(edit->track);
  if (!(edit->modebits & MB_PLAYALL))
  {
    editsolotracks();
  }
  else editunsolotracks();
  transportcommand(TC_POSITION,begin);
  transportcommand(TC_PLAY,0);
  if (!(functions.flags & FLAGS_CONTINUEATRIGHT)) inserttpcommand(end,TC_STOP);
}

/*
void sendclipnotes(edit,begin,end)

struct Edit *edit;
long begin,end;

{
    struct Event *event = edit->clip.events.first;
    struct Track *track;
    long modebits = edit->modebits;
    long time = functions.timenow;
    edit->modebits = MB_PLAYNOTES;
    doinglisten = 1;
    sendalltypes = 1;
    for (;event;event = event->next) {
        if (event->time >= begin) break;
    }
    if (modebits & MB_PLAYALL) time += 20;
    edit->listenstart = time;
    edit->listenend = time + (end - begin);
    edit->listenpoint = begin;
    for (;event;event = event->next) {
        if (event->time >= end) break;
        sendonenoteout(edit,(struct NoteEvent *)event,time + (event->time - begin));
    }
    if (modebits & MB_PLAYALL) {
        for (track = functions.tracklist;track;track = track->next) {
            if (track != edit->track) {
                if (!(track->mode & TRACK_MUTE)) {
                    event = track->clip.events.first;
                    for (;event;event = event->next) {
                        if (event->time >= begin) break;
                    }
                    for (;event;event = event->next) {
                        if (event->time >= end) break;
                        sendtotool(track->tool.next,(struct NoteEvent *)event,
                            time + (event->time - begin));
                    }
                }
            }
        }
    }
    edit->modebits = modebits;
    doinglisten = 0;
    sendalltypes = 0;
}
*/

void sendlistnotes(struct Edit *edit)

{
//    struct NoteEvent *event = edit->listfirst;
//    long modebits = edit->modebits;
//    long time = functions.timenow;
    long begin;
    if (edit->listfirst) begin = edit->listfirst->time;
    sendclipnotes(edit,begin,edit->listlast->time);
/*
    edit->modebits = MB_PLAYNOTES;
    doinglisten = 1;
    sendalltypes = 1;
    for (;event;event = event->next) {
        sendonenoteout(edit,(struct NoteEvent *)event,time + (event->time - begin));
        if (event == edit->listlast) break;
    }
    edit->modebits = modebits;
    doinglisten = 0;
    sendalltypes = 0;
*/
}

void fixbeginandend(edit)

register struct Edit *edit;

{
    register long temp;
    if (edit->begin > edit->end) {
        temp = edit->begin;
        edit->begin = edit->end;
        edit->end = temp;
    }
}

struct EventList *noteinwhichlist(edit,note)

struct Edit *edit;
struct NoteEvent *note;

{
    struct NoteEvent *scan;
    struct EventList *list;
    if (note) {
        if (note == edit->displaynote) {
            edit->displaynote = (struct NoteEvent *) note->next;
        }
        if ((note->type == EVENT_VOICE) || (note->type == EVENT_SYSX))
            list = &edit->clip.events;
        else if (note->type == EVENT_CHORD) list = &edit->clip.chords;
        else if (note->type == EVENT_LYRIC) list = &edit->clip.lyrics;
        else if (note->type == EVENT_KEY) list = &edit->clip.keys;
        else if (note->type == EVENT_TIMESIG)
            list = (struct EventList *) &edit->clip.timesig;
        else if (note->type == EVENT_RHYTHM) list = &edit->clip.rhythm;
        else if (note->type == EVENT_DYNAMICS) list = &edit->clip.dynamics;
        else return(0);
        for (scan = (struct NoteEvent *) list->first;scan;scan = scan->next) {
            if (scan == note) return(list);
        }
    }
    return(0);
}

removenote(edit,note)

struct Edit *edit;
struct NoteEvent *note;

{
    struct NoteEvent *last;
    struct EventList *list = noteinwhichlist(edit,note);
    if (!list) return(0);
    if (note == edit->displaynote) {
        edit->displaynote = (struct NoteEvent *) note->next;
    }
    if (list->point == (struct Event *) note)
        list->point = (struct Event *) note->next;
    if (list->first == (struct Event *) note)
        list->first = (struct Event *) note->next;
    else {
        last = (struct NoteEvent *)
            List_Pred((struct ListItem *)list->first,
                (struct ListItem *)note);
        if (last) {
            last->next = note->next;
        }
        else return(0);
    }
    tagnote((struct Edit *)edit,(struct NotateEvent *)note);
    note->next = 0;
    return(1);
}

void insertnote(edit,note)

register struct Edit *edit;
register struct Event *note;

{
    note->next = edit->clip.events.first;
    note = (struct Event *) sorteventlist(note);
    edit->clip.events.first = note;
    edit->clip.events.point = note;
    setdisplaynote(edit);
}

static unsigned char status[18] = { 0,0,0,0,0,0,0,MIDI_NOTEON,MIDI_NOTEON,
        MIDI_NOTEON,MIDI_NOTEON,MIDI_NOTEON,
        MIDI_PBEND, MIDI_MTOUCH, MIDI_PTOUCH, MIDI_CCHANGE, MIDI_PCHANGE,
        MIDI_SYSX };

short finddisplaytype(struct Edit *edit,short y)

{
    short base;
    long showbits = edit->showbits;
    char type = TYPE_LYRIC;
    y = y - TOPDATASTART;
    y += edit->topstart;
    base = edit->lyricbase;
    if ((showbits & SB_SYSEX) && (y > edit->patchbase))
    {
        type = TYPE_SYSEX;
        base = edit->sysexbase;
    }
    else if ((showbits & SB_PATCH) && (y > edit->controlbase))
    {
        type = TYPE_PATCH;
        base = edit->patchbase;
    }
    else if ((showbits & SB_CONTROL) && (y > edit->ptouchbase))
    {
        type = TYPE_CONTROL;
        base = edit->controlbase;
    }
    else if ((showbits & SB_PTOUCH) && (y > edit->mtouchbase))
    {
        type = TYPE_PTOUCH;
        base = edit->ptouchbase;
    }
    else if ((showbits & SB_MTOUCH) && (y > edit->pbendbase))
    {
        type = TYPE_MTOUCH;
        base = edit->mtouchbase;
    }
    else if ((showbits & SB_PBEND) && (y > edit->velocitybase))
    {
        type = TYPE_PBEND;
        base = edit->pbendbase;
    }
    else if ((showbits & SB_VELOCITY) && (y > edit->pianobase))
    {
        type = TYPE_VELOCITY;
        base = edit->velocitybase;
    }
    else if ((showbits & SB_PIANO) && (y > edit->staffbase))
    {
        type = TYPE_PIANO;
        base = edit->pianobase;
    }
    else if ((showbits & SB_STAFF) && (y > edit->tabbase))
    {
        type = TYPE_STAFF;
        base = edit->staffbase;
    }
    else if ((showbits & SB_TAB) && (y > edit->notationbase))
    {
        type = TYPE_TAB;
        base = edit->tabbase;
    }
    else if ((edit->showbits & SB_NOTATION) && (y > edit->timesigbase))
    {
        type = TYPE_NOTATION;
        base = edit->notationbase;
    }
    else if ((edit->showbits & SB_TIMESIG) && (y > edit->dynamicsbase))
    {
        type = TYPE_TIMESIG;
        base = edit->timesigbase;
    }
    else if ((edit->showbits & SB_DYNAMICS) && (y > edit->rhythmbase))
    {
        type = TYPE_DYNAMICS;
        base = edit->dynamicsbase;
    }
    else if ((edit->showbits & SB_RHYTHM) && (y > edit->keybase))
    {
        type = TYPE_RHYTHM;
        base = edit->rhythmbase;
    }
    else if ((edit->showbits & SB_KEY) && (y > edit->chordbase))
    {
        type = TYPE_KEY;
        base = edit->keybase;
    }
    else if ((edit->showbits & SB_CHORD) && (y > edit->lyricbase))
    {
        type = TYPE_CHORD;
        base = edit->chordbase;
    }
    else if (!(edit->showbits & SB_LYRIC))
    {
        type = 0;
        base = 0;
    }
    edit->selecttype = type;
    return(base);
}

static short positiontonote(edit,value,time)

struct Edit *edit;
short value;
long time;

{
    short octave;
    if (edit->selecttype == TYPE_PIANO) {
        value = value - TOPDATASTART;
        value = value + edit->topstart;
        value--;
        value -= edit->staffbase;
        value = IDivU(value,3);
        value = -value;
        value += edit->highnote;
        if ((value > edit->highnote) ||
            (value < edit->lownote)) return(0);
    }
    else if ((edit->selecttype == TYPE_STAFF) || (edit->selecttype == TYPE_NOTATION)){
        value = value - TOPDATASTART;
        value = value + edit->topstart;
        value -= 2;
        if (edit->selecttype == TYPE_STAFF) value = edit->staffbase - value;
        else value = edit->notationbase - value;
        value -= 11;
        value = IDivS(value,3);
        value += 21;
        octave = IDivS(value,7);
        value = value % 7;
        value = displayscaletotwelve(&edit->clip,time,value);
        value += IMulS(octave,12);
        value -= 60;
        if ((value < -41) || (value > 41)) return(0);
        value += edit->transcenter;
    }
    return(value);
}

static short newpositiontonote(edit,value,time)

struct Edit *edit;
short value;
long time;

{
    short octave, newvalue;
    char subpos;
    if (edit->selecttype == TYPE_PIANO) {
        value = value - TOPDATASTART;
        value = value + edit->topstart;
        value--;
        value -= edit->staffbase;
        value = IDivU(value,3);
        value = -value;
        value += edit->highnote;
        if ((value > edit->highnote) ||
            (value < edit->lownote)) return(0);
    }
    else if ((edit->selecttype == TYPE_STAFF) || (edit->selecttype == TYPE_NOTATION)){
        value = value - TOPDATASTART;
        value = value + edit->topstart;
        value -= 2;
        if (edit->selecttype == TYPE_STAFF) value = edit->staffbase - value;
        else value = edit->notationbase - value;
        value -= 11;
        subpos = value % 3;
        value = IDivS(value,3);
        value += 21;
        octave = IDivS(value,7);
        value = value % 7;
        value = displayscaletotwelve(&edit->clip,time,value);
        value += IMulS(octave,12);
        value -= 60;
        /* adjust for sharps and flats */
        if ((value < -41) || (value > 41)) return(0);
        value += edit->transcenter;
        newvalue = value;
        if (subpos == 0) newvalue -= 1;
        else if (subpos == 2) newvalue += 1;
        if (displaytwelvetoscale(&edit->clip,time,value,0) ==
                displaytwelvetoscale(&edit->clip,time,newvalue,0))
                value = newvalue;
    }
    return(value);
}

static char couldbeaccidental(struct Edit *edit,long time,short value)

{
    char offset;
    value -= edit->transcenter;
    value += 60;
    displaytwelvetoscale(&edit->clip,time,value,&offset);
    return(offset);
}

struct NoteEvent *findnote(edit,x,y,slop)

register struct Edit *edit;
long x,y;
long slop;

{
    short ypos;
    char type;
    long xpos;
    short base, width;
    struct NoteEvent *note;
    struct StringEvent *lyric;
    struct ChordEvent *chord;
    struct RhythmEvent *rhythm;
    struct DynamicsEvent *dy;
    short offset;
    char min;
    char apart;
    long time;
    setdisplaynote(edit);
    note = edit->displaynote;
    xpos = drawtotime(edit,x);
    if ((y < TOPDATASTART) || (y > edit->bottomedge)) return(0);
    if (x > edit->rightedge) return(0);
    ypos = y - TOPDATASTART;
    ypos += edit->topstart;
    base = finddisplaytype(edit,y);
    type = edit->selecttype;
    slop++;
    slop = slop << edit->shift;
    if (type < TYPE_NOTATION) {
        width = 16;
        if (type == TYPE_LYRIC)
            note = (struct NoteEvent *) edit->clip.lyrics.first;
        else if (type == TYPE_CHORD)
            note = (struct NoteEvent *) edit->clip.chords.first;
        else if (type == TYPE_KEY)
            note = (struct NoteEvent *) edit->clip.keys.first;
        else if (type == TYPE_RHYTHM)
            note = (struct NoteEvent *) edit->clip.rhythm.first;
        else if (type == TYPE_DYNAMICS) {
            width = 8;
            note = (struct NoteEvent *) edit->clip.dynamics.first;
            ypos = base - ypos;
            ypos = ypos << 2;
            ypos -= 12;
            if (ypos < 0) ypos = 0;
            if (ypos > 127) ypos = 127;
            edit->selectdata = ypos;
        }
        else if (type == TYPE_TIMESIG)
            note = (struct NoteEvent *) edit->clip.timesig.first;
        else return(0);
        width = width  << edit->shift;
        for (;note;note = note->next) {
            time = note->time;
            if (type == TYPE_LYRIC) {
                lyric = (struct StringEvent *) note;
                if (lyric->string)
                    width = (lyric->string->length - 2) << (2 + edit->shift);
            }
            else if ((type == TYPE_CHORD) || (type == TYPE_KEY)) {
                chord = (struct ChordEvent *) note;
                if (chord->chord && chord->chord->name) {
                    width = (chord->chord->name->length - 2) <<
                        (2 + edit->shift);
                }
            }
            else if (type == TYPE_RHYTHM) {
                rhythm = (struct RhythmEvent *) note;
                if (rhythm->rhythm && rhythm->rhythm->name) {
                    width = (rhythm->rhythm->name->length - 2) <<
                        (2 + edit->shift);
                }
            }
            if ((xpos >= (time - slop)) && (xpos <= (time + slop + width))) {
                if (type == TYPE_DYNAMICS) {
                    edit->selectnote = note;

                    dy = (struct DynamicsEvent *) note;
                    min = dy->value - ypos;
                    if (min < 0) min = -min;
                    for (;note; note = note->next) {
                        time = note->time;
                        if (time > (xpos + slop + width)) break;
                        dy = (struct DynamicsEvent *) note;
                        apart = dy->value - ypos;
                        if (apart < 0) apart = -apart;
                        if (apart < min) {
                            edit->selectnote = note;
                            min = apart;
                        }
                    }
                    return(edit->selectnote);
                }
                edit->selectnote = note;
                edit->selectdata = ypos;
                return(note);
            }
            if (time > (xpos + slop + 16)) break;
        }
        return(0);
    }
    if ((type == TYPE_PIANO) || (type == TYPE_STAFF)
        || (type == TYPE_NOTATION)) {
        if (edit->selectnote) {
            edit->selectdata = xpos - edit->selectnote->time;
        }
        ypos += TOPDATASTART;
        ypos -= edit->topstart;
        ypos = positiontonote(edit,ypos,xpos);
        if (type == TYPE_PIANO) offset = 0;
        else offset = -1;
        if (!ypos) return(0);
    }
    else if (type == TYPE_TAB)
    {
        if (edit->selectnote) edit->selectdata = xpos - edit->selectnote->time;
    }
    else {
        ypos = base - ypos;
        if (type == TYPE_PBEND) ypos = ypos << 1;
        else ypos = ypos << 2;
        if (ypos < 0) ypos = 0;
        if (ypos > 127) ypos = 127;
        edit->selectdata = ypos;
    }
    for (;note;note = note->next) {
        if (type == TYPE_NOTATION)
        {
                time = getnotetime(edit,note);
                if (time > (x + slop + 192)) return(0);
        }
        else
        {
                time = note->time;
                if (time > (xpos + slop)) return(0);
        }
        if ((type == TYPE_PIANO) || (type == TYPE_STAFF))
        {
            if (note->status == MIDI_NOTEON) {
                width = note->duration - 1;
                if (width < 4) width = 4;
                                                /* removed '+ slop' */
                if ((xpos >= time) && (xpos <= (time + width))) {
                    if (ypos == note->value) {
                        edit->selectnote = note;
                        edit->selectdata = xpos - time;
//                        tagnote(edit,note);
                        return(note);
                    }
                    else if (ypos == note->value + offset) {
                        if (couldbeaccidental(edit,time,note->value)) {
                            edit->selectnote = note;
                            edit->selectdata = xpos - time;
                            return(note);
                        }
                    }
                }
            }
        }
        else if (type == TYPE_TAB)
        {
                if (note->status == MIDI_NOTEON)
                {
                        if ((xpos >= time+(5<<edit->shift))
                         && (xpos <= time + (10<<edit->shift)) &&
                                (checkifonstring(y,note,edit)))
                        {
                                edit->selectnote = note;
                                edit->selectdata = xpos - note->time;;
                                return(note);
                        }
                }
        }
        else if (type == TYPE_NOTATION) {
//            if (isanote(note) || (isarest(note) && !(edit->editmode & E_FILL)))
                        if (isarealnote(note) && notatefull(note))
            {
                if (adjacentbitset(note)) time += 6;
                if ((x >= (time-(1)))
                 && (x <= (time + (6)))) {
                    if (ypos == note->value) {
                        edit->selectnote = note;
                        edit->selectdata = x - time;
//                        tagnote(edit,note);
                        return(note);
                    }
                    else if (ypos == note->value + offset) {
                        if (couldbeaccidental(edit,time,note->value)) {
                            edit->selectnote = note;
                            edit->selectdata = x - time;
//                            tagnote(edit,note);
                            return(note);
                        }
                    }
                }
            }
        }

        else if (type == TYPE_PATCH) {
            if ((xpos >= (time - slop)) && (xpos <= (time + slop + 8))) {
                if (note->status == MIDI_PCHANGE) {
                    edit->selectnote = note;
                    return(note);
                }
            }
        }
        else if (type == TYPE_SYSEX) {
            if ((xpos >= (time - slop)) && (xpos <= (time + slop + 24))) {
                if (note->status == MIDI_SYSX) {
                    edit->selectnote = note;
                    return(note);
                }
            }
        }
        else if (type == TYPE_CONTROL) {
            if ((xpos >= (time - slop)) && (xpos <= (time + slop + 8))) {
                if (note->status == MIDI_CCHANGE) {
                    if (note->value == edit->controlnum) {
                        edit->selectnote = note;
                        return(note);
                    }
                }
            }
        }
        else if (type == TYPE_PTOUCH) {
            if ((xpos >= (time - slop)) && (xpos <= (time + slop + 8))) {
                if (note->status == MIDI_PTOUCH) {
                    if ((note->value <= edit->highnote) &&
                        (note->value >= edit->lownote)) {
                        edit->selectnote = note;
                        return(note);
                    }
                }
            }
        }
        else if (type > TYPE_PIANO) {
            if ((xpos >= (time - slop)) && (xpos <= (time + slop))) {
                if (note->status == status[type]) {
                    edit->selectnote = note;
                    return(note);
                }
            }
        }
    }
    return(0);
}

static void choosedynamic(edit,dy)

struct Edit *edit;
struct DynamicsEvent *dy;

{
    struct DynamicsEvent *scan, *new;
    char imm;
    scan = (struct DynamicsEvent *) List_Pred((struct ListItem *)edit->clip.dynamics.first,(struct ListItem *)dy);
    if (scan) {
        imm = popupdynamicslope(edit->window);
        if ((dy->time - scan->time) > 12) {
            if (imm) {
                new = (struct DynamicsEvent *) allocevent();
                if (new) {
                    scan->next = new;
                    new->next = dy;
                    new->value = scan->value;
                    new->time = dy->time - 1;
                    new->type = EVENT_DYNAMICS;
                }
            }
        }
        else {
            if (!imm) removenote(edit,(struct NoteEvent *)scan);
        }
    }
}

void changetabvalue(struct Edit *edit)
{
    struct Window *window = edit->window;
    struct NoteEvent *note = edit->selectnote;
    struct NotateEvent initnote;
    long class = MOUSEMOVE;
    long code,flags;
    long d_mousex, d_mousey, wtopedge, sheight, value, origvalue;
    long y, orig_mousex, orig_mousey, x, temp;
    struct IntuiMessage *message;
    if (!note) return;
    memcpy(&initnote,note,sizeof(struct NotateEvent));
    blankpointer(window);
    wtopedge = window->TopEdge;
    sheight = functions.screen->Height/2;
    orig_mousex = window->MouseX;
    orig_mousey = window->MouseY;
    y = 0;
    settab(note); /* all this does is set NM_TABSET bit */
    value = origvalue = note->value;
    displayeventtype(edit,D_DELETE);
    displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
    x = note->time;
    flags = window->IDCMPFlags;
    ModifyIDCMP(window,flags|IDCMP_DELTAMOVE);
    ClearIntuiMessages(window);
    for (;((class == MOUSEMOVE) || (class == INTUITICKS));) {
        while (abs(y) >= 4)
        {
          if (y>0)
          {
            value -= 1;
            y -= 4;
          }
          else
          {
            value += 1;
            y += 4;
          }
          value = fitvalue(edit,note,value);
        }
        /* it is important to leave these similar if statements
         * unconsolidated, so that draging the mouse left/right
         * doesn't let value get higher as well
         */
        if ((locktonote(edit,x) != note->time))
        {
            displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
            note->time = locktonote(edit,x);
            tagnote((struct Edit *)edit,(struct NotateEvent *)note);
            sendnoteout(edit,note);
            displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
        }
        if (value != note->value)
        {
            displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
            note->value = value;
            if (edit->modebits & MB_LOCKTOKEY) {
                temp = noteinkey(&edit->clip,note);
                if (!temp) while (!temp)
                {
                                        if (note->value == 127) break;
                        note->value++;
                        temp = noteinkey(&edit->clip,note);
                }
            }
            tagnote((struct Edit *)edit,(struct NotateEvent *)note);
            sendnoteout(edit,note);
            displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
        }
//        message = (struct IntuiMessage *) newgetmymessage(window);
        message = (struct IntuiMessage *)GetIntuiMessage(window);
        class = message->Class;
        code = message->Code;
        d_mousex = message->MouseX;
        d_mousey = message->MouseY;
        ReplyMsg((struct Message *)message);
        y += d_mousey;
        if (edit->editmode & E_FILL)
        {
            x += (d_mousex<<edit->shift);
/*
            x = x << edit->shift;

            if (x < 0) {
                x = shifteditwindow(edit,(char)-1);
            }
            else if (x > edit->width) {
                x = shifteditwindow(edit,(char)1);
            }
            else x = x + edit->leftstart;
*/
        }
    }
    if ((class == MOUSEBUTTONS) && (code == MENUDOWN))
    {
      displayeventtype(edit,D_COMPLEMENT);
      memcpy(note,&initnote,sizeof(struct NotateEvent));
      displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
    }
    ModifyIDCMP(window,flags);
//    MovePointer(window, orig_mousex, orig_mousey);
    if (edit->editmode != E_FILL) wandpointer(window);
}

static void changeopenstring(struct Edit *edit)
{
    char controltext[10];
    struct RastPort *rp = edit->rp;
    unsigned short leftedge = edit->leftedge-20;
    unsigned short value, origvalue;
    long class = MOUSEMOVE;
    long code;
    short mousex, mousey, wtopedge, sheight;
    short y, oldmousey, base, string;
    struct IntuiMessage *message;
    blankpointer(edit->window);
    base = edit->tabbase - edit->topstart + TOPDATASTART - 10;
    wtopedge = edit->window->TopEdge;
    sheight = functions.screen->Height/2;
    mousex = edit->window->MouseX;
    mousey = edit->window->MouseY;
    y = 0;
    string = onwhatstring(edit->y, edit);
    if ((string < 0) || (string >= edit->tabstrings)) return;
    base -= TAB_STRINGWIDTH*(string);
    origvalue = value = edit->string[string];
    notetostring(&edit->clip,value,controltext);

    /* draw text in white to delete */
        smalltext(rp,leftedge,base,1,0,controltext);

        /* then draw in complementary color red */
        smalltext(rp,leftedge,base,-1,0,controltext);

    oldmousey = mousey;
    edit->initialpos = mousey;
    for (;((class == MOUSEMOVE) || (class == INTUITICKS));) {
        value = origvalue - y/4;
        if ((value != edit->string[string]) && (value >= 0) &&
                (value < 128))
        {
                smalltext(rp,leftedge,base,-1,0,controltext);
                edit->string[string] = value;
                notetostring(&edit->clip,value,controltext);
                smalltext(rp,leftedge,base,-1,0,controltext);
        }
        message = (struct IntuiMessage *) newgetmymessage(edit->window);
        class = message->Class;
        code = message->Code;
        mousex = message->MouseX;
        mousey = message->MouseY;
        y = mousey - oldmousey;
        ReplyMsg((struct Message *)message);
        if ((wtopedge + mousey) <= 10)
        {
                mousey += sheight;
                oldmousey += sheight;
                MovePointer(edit->window, mousex, mousey);
        }
        else if ((wtopedge + mousey) >= ((sheight*2) - 10))
        {
                mousey -= sheight;
                oldmousey -= sheight;
                MovePointer(edit->window, mousex, mousey);
        }
        if ((y/4) > origvalue) oldmousey = mousey - (origvalue*4);
        if ((y/4) < (origvalue-128))
                        oldmousey = mousey - ((origvalue-128)*4);
    }
    if ((class == MOUSEBUTTONS) && (code == MENUDOWN))
    {
        value = origvalue;
        if ((value != edit->string[string]) && (value >= 0) &&
                (value < 128))
        {
                smalltext(rp,leftedge,base,-1,0,controltext);
                edit->string[string] = value;
                notetostring(&edit->clip,value,controltext);
                smalltext(rp,leftedge,base,-1,0,controltext);
        }
    }
    mousex = edit->x;
    mousey = edit->y;
        MovePointer(edit->window, mousex, mousey);
        edit->initialpos = -1;
        /*plot in black */
        smalltext(rp,leftedge,base,0,0,controltext);
    seteditpointer(edit);
}

static void displaynewnotelength(struct Edit *edit,struct NoteEvent *note,
  USHORT oldduration)

{
    short l,b,r, r2;
    long t,t1;
    char offset;
    short color;
    struct RastPort *rp = edit->rp;
    long time = note->time;
    char drawstart;
    short value;
    if (!note) return;
    time -= (long) edit->leftstart;
    time = time >> edit->shift;
    l = time + edit->leftedge;
    if (oldduration < note->duration) color = 6; /* HIGHLIGHT */
    else color = 1; /* DELETE */

    SetDrMd(rp,JAM2);
    SetDrPt(rp,-1);
    if (edit->showbits & SB_PIANO)
    {
       if ((note->value > edit->highnote) ||
           (note->value < edit->lownote)) t1 = -1;
       else
       {
         t1 = edit->highnote - note->value;
         t1 = t1 * 3;
         t1 += edit->staffbase;
         t1++;
         if ((t1 <= edit->staffbase) || (t1 >= edit->pianobase))
           t1 = -1;
         else t1 -= edit->topstart;
         offset = 0;
       }
    }
    else t1 = -1;
    if (edit->showbits & SB_STAFF)
    {
        value = note->value;
        value -= edit->transcenter;
        value += 60;
        t = displaytwelvetoscale(&edit->clip,note->time,value,&offset);
        t = t - 21;
        t = IMulS(t,3);
        t += 11;
        t = edit->staffbase - t;
        if ((t <= edit->tabbase) || (t >= edit->staffbase)) t = -1;
        t -= edit->topstart;
    }
    else t = -1;
    r = l + (note->duration >> edit->shift);
    if (r >= edit->rightedge) r = edit->rightedge - 1;
    r2 = l + (oldduration >> edit->shift);
    if (time < 0) {
        l -= time;
        drawstart = 0;
    }
    else drawstart = 1;
    if (r2 > r)
    {
      l = r;
      r = r2;
    }
    else l = r2;
    if (l >= edit->rightedge) return;
    if (r > edit->rightedge) r = edit->rightedge;
    if (l <= r)
    {
      SetOPen(rp,color);
      SetAPen(rp,color);
      if (t >= 0)
      {
        t += TOPDATASTART;
        b = t + 2;
        if (b < edit->bottomedge)
        {
          newrectfill(rp,color,l,t,r,b,0);
        }
      }
      if (t1 >= 0)
      {
        t = t1;
        t += TOPDATASTART;
        b = t + 2;
        if (b < edit->bottomedge)
        {
          newrectfill(rp,color,l,t,r,b,0);
        }
      }
    }
}

alternote(struct Edit *edit,long x,long y)

{
    struct NoteEvent *note;
    struct RastPort *rp = edit->window->RPort;
    struct ChordEvent *chord;
    struct KeyEvent *key;
    struct RhythmEvent *rhythm;
    struct NoteEvent *oldnote;
    struct NoteEvent *scan;
    long endx;
    short type;
    long lastx;
    short started = 1;
    long endtime;
    if (edit->selecttype == TYPE_TAB) {
        if (edit->selectnote && (x>=edit->leftedge)) {
            changetabvalue(edit);
            return(1);
        }
        else if (x < edit->leftedge) {
            if (x > edit->leftedge-26) {
                edit->x = (short)x;
                edit->y = (short)y;
                changeopenstring(edit);
                return(2);
            }
            else {
                type = onwhatstring(y,edit);
                if ((type >= 0) && (type < edit->tabstrings)) {
                    y = edit->tabbase - edit->topstart + TOPDATASTART
                        - TAB_STRINGWIDTH*(type+1);
                    type = 1<<type;
                    if (edit->stringbits & type) {
                        edit->stringbits &= ~type;
                        smalltext(edit->rp,edit->leftedge-40,y-1,1,0,"ON ");
                        smalltext(edit->rp,edit->leftedge-40,y-1,0,0,"OFF");
                    }
                    else {
                        edit->stringbits |= type;
                        smalltext(edit->rp,edit->leftedge-40,y-1,1,0,"OFF");
                        smalltext(edit->rp,edit->leftedge-40,y-1,0,0,"ON ");
                    }
                }
            }
            return(1);
        }
    }
    if (edit->x) {
        if (x < edit->x) {
            endx = x;
            x = edit->x;
            edit->x = endx;
        }
        else endx = x;
    }
    else {
        endx = 0;
        edit->x = x;
        started = 0;
    }
    endtime = drawtotime(edit,x);
    lastx = edit->x;
    for (;;) {
        type = edit->selecttype;
        oldnote = edit->selectnote;
        if(oldnote && (edit->editmode & (E_DRAW|E_FILL)) &&
           (edit->modebits & MB_LOCKWAND) &&
           ((edit->selecttype < TYPE_VELOCITY) ||
            (edit->selecttype > TYPE_CONTROL)))
        // Added missing paranthes. -Kjetil M.
        {
          note = oldnote;
        }
        else
        {
          note = findnote(edit,edit->x,y,0);
          if (!note && (edit->editmode & (E_DRAW|E_FILL)) &&
            (edit->modebits & MB_LOCKWAND)) {
            if ((edit->selecttype == TYPE_STAFF) ||
                (edit->selecttype == TYPE_PIANO)) note = oldnote;
          }
        }
        if (started && (edit->selecttype != type)) {
            edit->selecttype = type;
            edit->x++;
            if (edit->x >= x) break;
            continue;
        }
        started = 1;
        if ((edit->selecttype > TYPE_PIANO) && (edit->selecttype < TYPE_PATCH)) {
            if ((x > LEFTDATASTART) && (x < edit->rightedge) &&
                (y > TOPDATASTART) && (y < edit->bottomedge)) {
                SetDrMd(rp,JAM2);
                SetAPen(rp,3);
                Move(rp,lastx,y);
                Draw(rp,edit->x,y);
            }
            lastx = edit->x;
        }
        while (note) {
            switch (edit->selecttype) {
                case TYPE_LYRIC :
                    editlyrics((struct StringEvent *)note);
                    return(1);
                    break;
                case TYPE_CHORD :
                    chord = (struct ChordEvent *) note;
                    chord->root = popupkey(edit->window,chord->root);
                    chord->chord = (struct Chord *)
                        popupchord(chord->chord);
                    setchordsharporflat(&edit->clip,chord);
                    sendchordout(edit,chord);
                    return(1);
                    break;
                case TYPE_KEY :
                    key = (struct KeyEvent *) note;
                    key->root = popupkey(edit->window,key->root);
                    key->scale = (struct Chord *)
                        popupscale(key->scale);
                    key->flats = popupsharporflat(edit->window,
                        key->root,key->flats);
                    key->flats = (sharpflatcount(key) < 0);
                    updateobject(&functions.masteredit,edit);
                    return(1);
                    break;
                case TYPE_RHYTHM :
                    rhythm = (struct RhythmEvent *) note;
                    rhythm->rhythm = (struct Rhythm *)
                        popuprhythm(rhythm->rhythm);
                    return(1);
                    break;
                case TYPE_DYNAMICS :
                    choosedynamic(edit,note);
                    return(1);
                    break;
                case TYPE_TIMESIG :
                    changetimesig(&edit->clip,note->time);
                    return(1);
                    break;
                case TYPE_PIANO :
                case TYPE_STAFF :
                {
                    long newduration;
                    static short oldduration = 0;
                    static long oldposition = 0;
                    static short firstduration = 0;
                    if (edit->fillsequence) {
                        if (edit->fillsequence == note) {
                            newduration = firstduration + ((edit->window->MouseX
                              - oldposition)<<edit->shift);
                            if (newduration > 0) note->duration = newduration;
                            else note->duration = 1;
                            if (note->duration > 0xFF00) note->duration =
                            0xFF00;
                            changeduration((struct Edit *)edit,(struct NotateEvent *)note);
                            displaynewnotelength(edit,note,oldduration);
                            oldduration = note->duration;
                        }
                        else {
                            edit->selectnote = note;
                            displayeventtype(edit,D_DELETE);
                            note->duration = edit->fillsequence->duration;
                            note->data = edit->fillsequence->data;
                            displayeventtype(edit,D_HIGHLIGHT);
                        }
                    }
                    else {
                        edit->selectnote = note;
                        displayeventtype(edit,D_HIGHLIGHT);
                        edit->fillsequence = note;
                        oldposition = edit->window->MouseX;
                        oldduration = firstduration = note->duration;
                    }
                    displayeditlength((struct Edit *)edit,note->time,note->duration);
                    tagnote((struct Edit *)edit,(struct NotateEvent *)note);
                    edit->x = x;
                    break;
                }
                case TYPE_NOTATION :
                    displayeventtype(edit,D_DELETE);
                    initnote((struct Edit *)edit,(struct NotateEvent *)note,1);
                    displayeditlength(edit,note->time,note->duration);
                    displayeventtype(edit,D_HIGHLIGHT);
                    tagnote((struct Edit *)edit,(struct NotateEvent *)note);
                    break;
                case TYPE_VELOCITY :
                    note->velocity = edit->selectdata;
                    sendnoteout(edit,note);
                    break;
                case TYPE_PBEND :
                    note->value = 0;
                    note->velocity = edit->selectdata;
                    break;
                case TYPE_MTOUCH :
                    note->value = edit->selectdata;
                    break;
                case TYPE_PTOUCH :
                    note->velocity = edit->selectdata;
                    break;
                case TYPE_CONTROL :
                    if (note->value == edit->controlnum)
                        note->velocity = edit->selectdata;
                    break;
                case TYPE_PATCH :
                    scrollpatch(edit);
                    return(1);
                case TYPE_SYSEX :
                    editsysex(edit->track,(struct StringEvent *)note);
                    return(1);
            }
            edit->selectnote = note;
            if (edit->selecttype > TYPE_PIANO) {
                scan = note->next;
                for (;scan;scan = scan->next) {
                    if (scan->time >= endtime) {
                        scan = 0;
                        break;
                    }
                    if (scan->status == note->status) break;
                }
                note = scan;
            }
            else note = 0;
        }
        if (!endx) break;
        edit->x++;
        if (edit->x >= x) break;
    }
    if (endx) edit->x = endx;
    edit->y = y;
    return(0);
}

static unsigned char findtouchnote(edit,time)

struct Edit *edit;
unsigned long time;

{
    struct NoteEvent *event;
    char high, low;
    unsigned char last;
    high = edit->highnote;
    low = edit->lownote;
    event = edit->displaynote;
    last = 0;
    for (;event;event = event->next) {
        if (event->time > time) break;
        if (event->status == MIDI_NOTEON) {
            if ((event->value >= low) && (event->value <= high)) {
                last = event->value;
                if ((event->time <= time) &&
                    ((event->time + event->duration) >= time))
                        return(event->value);
            }
        }
    }
    return(last);
}

locktonote(edit,time)

struct Edit *edit;
long time;

{
    long notelen;
    long nextmeasure;
    long basetime;
    long notetime;
    if (edit->modebits & MB_HIRES) return(time);
    else if (edit->modebits & MB_NOTATERES) {
        if (edit->selectnote) notelen = (192 >> getnoteres(edit->selectnote));
        else notelen = (192 >> (edit->notateres & 3));
        if (edit->notateres & 0x80) {
            notelen = notelen / 3;
            notelen = notelen << 1;
        }
    }
    else if (edit->modebits & MB_LOCKTORHYTHM) {
        struct NotateEvent dummy;
        dummy.time = time;
        if (noteinrhythm((struct Clip *)&edit->clip,(struct NoteEvent *)&dummy)) {
            time = dummy.time;
        }
        return(time);
    }
    else if (edit->modebits & MB_ALIGNRES) {
        time = (long) converttime(&edit->clip,(unsigned long)time);
        return(time);
    }
    else {
        edit->modebits |= MB_NOTERES;
        notelen = edit->insertlength;
    }
    nextmeasure = timetomeasure(&edit->clip,time);
    basetime = measuretotime(&edit->clip,nextmeasure);
//    nextmeasure = measuretotime(&edit->clip,nextmeasure+1);
    time -= basetime;
    if (edit->selectnote) {
        notetime = edit->selectnote->time;
        notetime -= basetime;
        notetime %= notelen;
    }
    else notetime = 0;
//    time = (time + (notelen >> 1)) / notelen;
    time /= notelen;
    time *= notelen;
    time += basetime;
//    if (time > nextmeasure) time = nextmeasure;
    time += notetime;
    return(time);
}

dragnote(edit,x,y)

struct Edit *edit;
long x,y;

{
    struct NoteEvent *note, *copy;
    long value;
    short xc, yc, string;
    struct DynamicsEvent *dy;
    if (edit->selectnote) {
        if (edit->shiftkey) {
            if (edit->shiftkey & 0x80) x = edit->x;
            else if (edit->shiftkey & 0x40) y = edit->y;
            else {
                xc = edit->x - x;
                if (xc < 0) xc = -xc;
                xc = xc >> 1;
                yc = edit->y - y;
                if (yc < 0) yc = -yc;
                if (xc > yc) edit->shiftkey |= 0x40;
                else if (yc > xc) edit->shiftkey |= 0x80;
            }
        }
    }
    edit->x = x;
    edit->y = y;
    if (!edit->selectnote) {
        note = findnote(edit,x,y,1);
        if (note) {
            edit->initialpos = y;
            edit->comparepos = y;
            if (!(edit->editmode & E_COPY))
            {
              tagnote((struct Edit *)edit,(struct NotateEvent *)note);
              initnote((struct Edit *)edit,(struct NotateEvent *)note,0);
            }
            else {
                copy = (struct NoteEvent *) dupeevent(note);
                if (copy) {
                    copy->next = 0;
                    edit->selectnote = copy;
                    switch (edit->selecttype) {
                        case TYPE_LYRIC :
                            edit->clip.lyrics.first = (struct Event *)
                                List_Cat((struct ListItem *)edit->clip.lyrics.first,(struct ListItem *)copy);
                            break;
                        case TYPE_CHORD :
                            edit->clip.chords.first = (struct Event *)
                                List_Cat((struct ListItem *)edit->clip.chords.first,(struct ListItem *)copy);
                            break;
                        case TYPE_KEY :
                            edit->clip.keys.first = (struct Event *)
                                List_Cat((struct ListItem *)edit->clip.keys.first,(struct ListItem *)copy);
                            break;
                        case TYPE_RHYTHM :
                            edit->clip.rhythm.first = (struct Event *)
                                List_Cat((struct ListItem *)edit->clip.rhythm.first,(struct ListItem *)copy);
                            break;
                        case TYPE_DYNAMICS :
                            edit->clip.dynamics.first = (struct Event *)
                                List_Cat((struct ListItem *)edit->clip.dynamics.first,(struct ListItem *)copy);
                            break;
                        case TYPE_TIMESIG :
                            edit->clip.timesig.first = (struct TimeSigEvent *)
                                List_Cat((struct ListItem *)edit->clip.timesig.first,(struct ListItem *)copy);
                            break;
                        case TYPE_NOTATION :
                        case TYPE_TAB :
                        case TYPE_STAFF :
                        case TYPE_PIANO :
                        case TYPE_VELOCITY :
                        case TYPE_PBEND :
                        case TYPE_MTOUCH :
                        case TYPE_PTOUCH :
                        case TYPE_CONTROL :
                        case TYPE_PATCH :
                        case TYPE_SYSEX :
                            edit->clip.events.first = (struct Event *)
                                List_Cat((struct ListItem *)edit->clip.events.first,(struct ListItem *)copy);
                            break;
                    }
                }
            }
            if (edit->editmode & E_DRAG) {
                displayeventtype(edit,D_DELETE);
            }
            displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
        }
    }
    if (note = edit->selectnote) {
        blankpointer(edit->window);
        if (edit->selecttype == TYPE_TAB) {
            string = onwhatstring(y,edit);
            if ( (string != notestring(note) &&
                    (string < edit->tabstrings) && (string >= 0))) {
                displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
                putonstring(edit,note,string);
                displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
                edit->initialpos = -1;
                sendnoteout(edit,note);
                return(1);
            }
        }
        y = edit->initialpos + (y - edit->comparepos)/4;        /* quadruple vertical mouse resolution */
            edit->y = y;
        x -= edit->leftedge;
        x = x << edit->shift;
        if (x < 0) {
            x = shifteditwindow(edit,(char)-1);
            refreshedit(edit,R_DRAWWINDOW);
        }
        else if (x > edit->width) {
            x = shifteditwindow(edit,(char)1);
            refreshedit(edit,R_DRAWWINDOW);
        }
        else x = x + edit->leftstart;
        if ((edit->selecttype == TYPE_PIANO) ||
            (edit->selecttype == TYPE_STAFF) ||
            (edit->selecttype == TYPE_NOTATION) ||
            (edit->selecttype == TYPE_TAB)) {
            x -= edit->selectdata;
        }
        x = locktonote(edit,x);
        if ((edit->selecttype == TYPE_PIANO) ||
            (edit->selecttype == TYPE_STAFF) ||
            (edit->selecttype == TYPE_NOTATION)) {
            value = newpositiontonote(edit,y,note->time);
            if (!value) value = note->value;
            if (edit->modebits & MB_LOCKTOKEY) {
                value = twelvetoscale(&edit->clip,x,value,0);
                value = scaletotwelve(&edit->clip,x,value);
            }
            value &= 0x7F;
        }
        else if (edit->selecttype == TYPE_DYNAMICS) {
            value = y - TOPDATASTART;
            value += edit->topstart;
            value = edit->dynamicsbase - value;
            value = value << 2;
            if (value < 0) value = 0;
            if (value > 127) value = 127;
            dy = (struct DynamicsEvent *) note;
            if ((x > (dy->lasttime - (10 << edit->shift))) &&
                (x <= (dy->lasttime))) x = dy->lasttime + 1;
            else if (dy->next && (x >= dy->nexttime) &&
                (x < dy->nexttime + (10 << edit->shift))) x = dy->nexttime - 1;
            if ((value != dy->value) || (x != note->time)) {
                displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
                dy->value = value;
                note->time = x;
                displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
                displayedittime(edit,x);
                return(1);
            }
        }
        else value = note->value;
        if ((value != note->value) || (x != note->time)) {
            displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
            note->value = value;
            note->time = x;
            initnote((struct Edit *)edit,(struct NotateEvent *)note,0);
            displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
            tagnote((struct Edit *)edit,(struct NotateEvent *)note);
            sendnoteout(edit,note);
            return(1);
        }
    }
    return(0);
}

void dragthisnote(edit)

struct Edit *edit;

{
    long class = MOUSEMOVE;
    long code;
    short mousex, mousey, wtopedge, sheight;
    struct IntuiMessage *message;
    struct NotateEvent initnote;
    wtopedge = edit->window->TopEdge;
    sheight = functions.screen->Height/2;
//    mousex = edit->window->MouseX;
//    mousey = edit->window->MouseY;
    edit->selectdata = 0;
//    edit->initialpos = -1;
    message = (struct IntuiMessage *) newgetmymessage(edit->window);
    class = message->Class;
    mousex = message->MouseX;
    mousey = message->MouseY;
    ReplyMsg((struct Message *)message);
    edit->initialpos = mousey;
    edit->comparepos = mousey;
    if (edit->selectnote)
    {
      memcpy(&initnote,edit->selectnote,sizeof(struct NotateEvent));
    }
    else initnote.type = 0;
    for (;((class == MOUSEMOVE) || (class == INTUITICKS));) {
        dragnote(edit,mousex,mousey);
        if ((!initnote.type) && edit->selectnote)
        {
          memcpy(&initnote,edit->selectnote,sizeof(struct NotateEvent));
        }
//        if (edit->initialpos == -1) {edit->initialpos = mousey; edit->comparepos = mousey;}
        if ((wtopedge + mousey) <= 10)
        {
                mousey += sheight;
                edit->comparepos += sheight;
                MovePointer(edit->window, mousex, mousey);
        }
        else if ((wtopedge + mousey) >= (sheight*2 - 10))
        {
                mousey -= sheight;
                edit->comparepos -= sheight;
                MovePointer(edit->window, mousex, mousey);
        }
        message = (struct IntuiMessage *) newgetmymessage(edit->window);
        class = message->Class;
        code = message->Code;
        mousex = message->MouseX;
        mousey = message->MouseY;
        ReplyMsg((struct Message *)message);
        if (edit->editmode&E_MAGNIFY) refresheditnote(edit);
//        refreshedit(edit,R_DRAWNOTATION);
    }
    if (edit->selectnote)
    {
      if ((class == MOUSEBUTTONS) && (code == MENUDOWN))
      {
        displayeventtype(edit,D_COMPLEMENT);
        if (initnote.type)
          memcpy(edit->selectnote,&initnote,sizeof(struct NotateEvent));
        if ((edit->editmode & E_COPY) && edit->selectnote)
        {
          List_Remove((struct ListItem *)edit->clip.events.first,(struct ListItem *)edit->selectnote);
          freeevent(edit->selectnote);
          edit->selectnote = 0;
        }
        else displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
      }
      else
        tagnote((struct Edit *)edit,(struct NotateEvent *)edit->selectnote);
    }
//      MovePointer(edit->window, mousex, edit->initialpos+(mousey-edit->comparepos)/4);
        edit->initialpos = -1;
    if (edit->editmode & E_FILL) pencilpointer(edit->window);
    else if (edit->editmode & E_COPY) dupepointer(edit->window);
    else handpointer(edit->window);
}

void wandthisnote(edit)

struct Edit *edit;

{
    long class = MOUSEMOVE;
    long code;
    struct NotateEvent initnote;
    long mousex, mousey;
    struct IntuiMessage *message;
    struct NoteEvent *fillsequence = edit->fillsequence;
    struct NoteEvent *note = edit->selectnote;
    if (!note) return;
    memcpy(&initnote,note,sizeof(struct NotateEvent));
    wandpointer(edit->window);
    edit->selectdata = drawtotime(edit,edit->window->MouseX) - note->time;
    if (edit->editmode != E_FILL) edit->fillsequence = note;
    else edit->fillsequence = 0;
    if (edit->selectdata > note->duration) {
        edit->notelen = 1;
    }
    else edit->notelen = note->duration - edit->selectdata;
    displayeventtype(edit,D_HIGHLIGHT);
    for (;((class == MOUSEMOVE) || (class == INTUITICKS));) {
        message = (struct IntuiMessage *) newgetmymessage(edit->window);
        class = message->Class;
        code = message->Code;
        mousex = edit->window->MouseX;
        mousey = edit->window->MouseY;
        ReplyMsg((struct Message *)message);
        if (alternote(edit,mousex,mousey)) break;
    }
    if ((class == MOUSEBUTTONS) && (code == MENUDOWN))
    {
      displayeventtype(edit,D_COMPLEMENT);
      memcpy(note,&initnote,sizeof(struct NotateEvent));
      displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
    }
    edit->fillsequence = fillsequence;
    if (edit->editmode & E_FILL) pencilpointer(edit->window);
    else wandpointer(edit->window);
}

fillnote(edit,x,y)

struct Edit *edit;
long x,y;

{
    struct NoteEvent *note;
    long temp;
    short inc;
    struct ChordEvent *chord;
    struct KeyEvent *key;
    struct RhythmEvent *rhythm;
    struct DynamicsEvent *dynamics, *scan;
    long time;
    short type;
    long oldx;
    temp = y - TOPDATASTART;
    temp += edit->topstart;
    oldx = x;
    x -= edit->leftedge;
    x = x << edit->shift;
    if ((x < 0) || (x > edit->width)) return(0);
    edit->selectnote = 0;
    x = locktonote(edit,x);
    x = x >> edit->shift;
    x += edit->leftedge;
    if (temp <= edit->timesigbase) {
        time = drawtotime(edit,x);
        finddisplaytype(edit,y);
        if (edit->selecttype == TYPE_LYRIC) {
            addlyrics(&edit->clip,time);
            edit->newlyric = (struct StringEvent *) edit->clip.lyrics.first;
        }
        else if (edit->selecttype == TYPE_VELOCITY) return(0);
        else if (edit->selecttype == TYPE_CHORD) {
            if (!edit->newchord && functions.chordlist) {
                chord = (struct ChordEvent *) allocevent();
                if (chord) {
                    chord->time = time;
                    chord->type = EVENT_CHORD;
                    chord->root = popupkey(edit->window,20);
                    if (chord->root < 20) {
                        key = (struct KeyEvent *) timetokey(&edit->clip,time);
                        chord->chord = 0;
                        if (key) {
                            chord->chord = (struct Chord *)
                                matchchordtokey(chord->root,key);
                        }
                        if (!chord->chord)
                            chord->chord = (struct Chord *) popupchord(0);
                    }
                    if ((chord->root < 12) && chord->chord) {
                        edit->newchord = chord;
                        setchordsharporflat(&edit->clip,chord);
                        edit->selectnote = (struct NoteEvent *) chord;
                        sendchordout(edit,chord);
                    }
                    else {
                        freeevent(chord);
                        chord = 0;
                    }
                }
            }
        }
        else if (edit->selecttype == TYPE_KEY) {
            if (!edit->newkey && functions.scalelist) {
                key = (struct KeyEvent *) allocevent();
                if (key) {
                    key->time = time;
                    key->type = EVENT_KEY;
                    key->root = popupkey(edit->window,20);
                    if (key->root < 20) {
                        key->scale = (struct Chord *)
                            popupscale(0);
                        if (key->scale)
                        {
                          key->flats = popupsharporflat(edit->window,
                            key->root,0);
                          key->flats = (sharpflatcount(key) < 0);
                        }
                    }
                    if ((key->root < 12) && key->scale) {
                        edit->newkey = key;
                        edit->selectnote = (struct NoteEvent *) key;
                        updateobject(&functions.masteredit,edit);
                    }
                    else freeevent(key);
                }
            }
        }
        if (edit->selecttype == TYPE_RHYTHM) {
            if (!edit->newrhythm) {
                rhythm = (struct RhythmEvent *) allocevent();
                if (rhythm) {
                    rhythm->time = time;
                    rhythm->type = EVENT_RHYTHM;
                    rhythm->rhythm = (struct Rhythm *)
                        popuprhythm(0);
                    if (rhythm->rhythm) {
                        edit->newrhythm = rhythm;
                        edit->selectnote = (struct NoteEvent *) rhythm;
                    }
                    else freeevent(rhythm);
                }
            }
        }
        if (edit->selecttype == TYPE_DYNAMICS) {
            if (!edit->newdynamics) {
                dynamics = (struct DynamicsEvent *) allocevent();
                if (dynamics) {
                    dynamics->time = time;
                    dynamics->type = EVENT_DYNAMICS;
                    y = y - TOPDATASTART;
                    y += edit->topstart;
                    y = edit->dynamicsbase - y;
                    y = y << 2;
                    if (y < 0) y = 0;
                    if (y > 127) y = 127;
                    dynamics->value = y;
                    edit->newdynamics = dynamics;
                    edit->selectnote = (struct NoteEvent *) dynamics;
                    scan = (struct DynamicsEvent *) edit->clip.dynamics.first;
                    if (!scan && time) {
                        dynamics = (struct DynamicsEvent *) allocevent();
                        if (dynamics) {
                            dynamics->time = 0;
                            dynamics->type = EVENT_DYNAMICS;
                            dynamics->value = y;
                            dynamics->next = edit->newdynamics;
                            edit->newdynamics = dynamics;
                        }
                    }
                    for (;scan;scan = scan->next) {
                        if (scan->time == time) {
                            if (!time) scan->time++;
                            else scan->time--;
                        }
                        if (scan->next) {
                            if (scan->next->time >= time) break;
                        }
                        else break;
                    }
                    if (scan) {
                        if (popupdynamicslope(edit->window)) {
                            dynamics = (struct DynamicsEvent *) allocevent();
                            if (dynamics) {
                                dynamics->time = time - 1;
                                dynamics->type = EVENT_DYNAMICS;
                                dynamics->value = scan->value;
                                dynamics->next = edit->newdynamics;
                                edit->newdynamics = dynamics;
                            }
                        }
                    }
                    return(1);
                }
            }
        }
        if (edit->selecttype == TYPE_TIMESIG) {
            changetimesig(&edit->clip,time);
            edit->newtimesig = edit->clip.timesig.first;
        }
        return(1);
    }
    inc = 0;
    edit->x = x;
    edit->selecttype = 0;
    inc = inc >> edit->shift;
    for (;; edit->x += inc) {
        time = drawtotime(edit,edit->x);
        if (time < edit->leftstart) {
            edit->x = x;
            edit->y = y;
            return(0);
        }
        for (note = edit->fillsequence;note;note = note->next) {
            if (note->time == time) {
                edit->x = x;
                edit->y = y;
                return(0);
            }
        }
        type = edit->selecttype;
        if ((type < TYPE_VELOCITY) && (edit->modebits & MB_HIRES)) {
            note = findnote(edit,oldx,y,1);
        }
        else note = findnote(edit,edit->x,y,1);
        if (type && (type != edit->selecttype)) {
            edit->selecttype = type;
            return(0);
        }
        if (!note && (edit->selecttype != TYPE_VELOCITY)) {
            note = (struct NoteEvent *) allocevent();
            if (note) {
                note->status = status[edit->selecttype];
                note->next = edit->fillsequence;
                note->time = time;
                if (edit->selecttype == TYPE_SYSEX)
                    note->type = EVENT_SYSX;
                else note->type = EVENT_VOICE;
                edit->selectnote = note;
                edit->fillsequence = note;
                switch (note->status) {
                    case MIDI_NOTEON :
                        if (edit->selecttype == TYPE_TAB)
                        {
                                short string = onwhatstring(y,edit);
                                if ((string >= 0) && (string < edit->tabstrings))
                                {
                                        temp = edit->string[string] + edit->tabposition;
                                        putonstring(edit,note,string);
                                }
                                else temp = 0;
                        }
                        else temp = positiontonote(edit,y,time);
                        note->value = temp;
                        if (temp && (edit->modebits & MB_LOCKTOKEY)) {
                            temp = noteinkey(&edit->clip,note);
                            if (!temp) while (!temp)
                            {
                                if (note->value == 127) break;
                                note->value++;
                                temp = noteinkey(&edit->clip,note);
                            }
                        }
                        if (temp && (edit->modebits & MB_LOCKTORHYTHM)) {
                            temp = noteinrhythm(&edit->clip,note);
                        }
                        if (!temp) {
                            edit->x = x;
                            edit->y = y;
                            edit->selectnote = 0;
                            edit->fillsequence = note->next;
                            freeevent(note);
                            return(0);
                        }
                        initnote((struct Edit *)edit,(struct NotateEvent *)note,2);
                        note->velocity = edit->insertvelocity;
                        displayeventtype(edit, D_DELETE);
                        displayeventtype(edit,D_HIGHLIGHT|D_COMPLEMENT);
                        sendnoteout(edit,note);
                        if (edit->selecttype == TYPE_TAB){
                            changetabvalue(edit);
                        }
                        else if (edit->modebits & MB_PENCILWAND) {
                            if (edit->selecttype != TYPE_NOTATION) {
                                edit->selectnote = note;
                                wandthisnote(edit);
                            }
                        }
                        tagnote((struct Edit *)edit,(struct NotateEvent *)note);
                        return(2);
                        break;
                    case MIDI_CCHANGE :
                        note->value = edit->controlnum;
                        break;
                    case MIDI_PTOUCH :
                        note->value = findtouchnote(edit,time);
                        if (!note->value) note->value = edit->lownote;
                        break;
                }
            }
        }
        else if (note) {
            if ((edit->selecttype >= TYPE_NOTATION) &&
                (edit->selecttype <= TYPE_PIANO)) {
                if (edit->modebits & MB_PENCILDRAG) {
                    initnote((struct Edit *)edit,(struct NotateEvent *)note,0);
//                    displayeventtype(edit,D_DELETE);
//                    displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
                    sendnoteout(edit,note);
//                    dragthisnote(edit);
                    tagnote((struct Edit *)edit,(struct NotateEvent *)note);
                    return(2);
                }
                else if (edit->modebits & MB_PENCILWAND) {
                    displayeventtype(edit,D_HIGHLIGHT);
                    sendnoteout(edit,note);
                    wandthisnote(edit);
                    tagnote((struct Edit *)edit,(struct NotateEvent *)note);
                    return(1);
                }
            }
        }
        if (note && ((note->type == EVENT_VOICE) || (note->type == EVENT_SYSX))) {
            switch (edit->selecttype) {
                case TYPE_VELOCITY :
                    note->velocity = edit->selectdata;
                    break;
                case TYPE_PBEND :
                    note->value = 0;
                    note->velocity = edit->selectdata;
                    break;
                case TYPE_MTOUCH :
                    note->value = edit->selectdata;
                    break;
                case TYPE_PTOUCH :
                    note->velocity = edit->selectdata;
                    break;
                case TYPE_CONTROL :
                    if (note->value == edit->controlnum)
                        note->velocity = edit->selectdata;
                    break;
                case TYPE_PATCH :
                    displayeventtype(edit,D_HIGHLIGHT);
                    if (!scrollpatch(edit)) {
                        if (note == edit->fillsequence) {
                            edit->selectnote = 0;
                            edit->fillsequence = 0;
                            freeevent(note);
                        }
                    }
                    return(1);
                case TYPE_SYSEX :
                    displayeventtype(edit,D_HIGHLIGHT);
                    if (editsysex(edit->track,(struct StringEvent *)note)) {
                        if (note == edit->fillsequence) {
                            edit->selectnote = 0;
                            edit->fillsequence = 0;
                            freeevent(note);
                        }
                    }
                    return(1);
            }
            displayeventtype(edit,D_HIGHLIGHT);
            sendnoteout(edit,note);
        }
        if (!inc) break;
        else if (inc > 0) {
            if (edit->x >= x) break;
        }
        else if (edit->x <= x) break;
    }
    edit->x = x;
    edit->y = y;
    return(0);
}

static struct Event *padeditlist(list)

struct Event *list;

{
    struct Event *top = 0;
    struct Event *next;
    struct Tool *tool;
    struct Event *scan;
    for (;list;) {
        next = list->next;
        list->next = 0;
        list->type |= EVENT_PADEDIT;
        tool = list->tool;
        list = (*tool->toolmaster->processevent) (list);
        if (scan = list) {
            for (;scan;scan = scan->next) {
                if (scan->tool == tool->branch) scan->type |= EVENT_BRANCH;
            }
            top = (struct Event *) List_Cat((struct ListItem *)top,(struct ListItem *)list);
        }
        list = next;
    }
    return(top);
}

static struct Event *processlist(tool,list)

struct Tool *tool;
struct Event *list;

{
    struct Event *dolist = 0;
    struct Event *dontdolist = 0;
    struct Event *next;
    for (;list;) {
        next = list->next;
        if (list->tool == tool) {
            list->next = dolist;
            dolist = list;
        }
        else {
            list->next = dontdolist;
            dontdolist = list;
        }
        list = next;
    }
    if (dolist) {
        dolist = (struct Event *) sorteventlist(dolist);
        for (list = dolist;list;list = list->next) list->tool = tool->next;
    }
    dolist = (struct Event *) (*tool->toolmaster->processlist)(tool,dolist,
        functions.padcutin,functions.padcutout);
    dolist = (struct Event *) List_Cat((struct ListItem *)dolist,(struct ListItem *)dontdolist);
    return(dolist);
}

static struct Tool endtool;

static struct Event *stripgarbage(event)

struct Event *event;

{
    struct Event *next;
    struct Event *keep = 0;
    for (;event;) {
        next = event->next;
        event->next = 0;
        if (!event->tool) {
            freeevent(event);
        }
//        if (!event->tool || (event->status == MIDI_NOTEOFF)) {
//            freeevent(event);
//        }
        else {
            event->next = keep;
            keep = event;
        }
        event = next;
    }
    keep = (struct Event *) sorteventlist(keep);
    return(keep);
}

struct Event *padeditevent(event,tool)

struct Event *event;
struct Tool *tool;

{
    struct Event *morelist, *donelist, *next;
    tool->next = (struct Tool *) &endtool;
    memset((char *)&endtool,0,sizeof(struct Tool));
    morelist = event;
    donelist = 0;
    if (event) {
        for (;event;event = event->next) event->tool = tool;
    }
    morelist = stripgarbage(morelist);
    if (!morelist) {
        if (tool->toolmaster->processlist) {
            morelist = processlist(tool,0);
            event = stripgarbage(morelist);
            morelist = 0;
            for (;event;) {
                next = event->next;
                event->next = 0;
                if (event->tool == &endtool) {
                    event->tool = 0;
                    event->next = donelist;
                    donelist = event;
                }
                else {
                    event->next = morelist;
                    morelist = event;
                }
                event = next;
            }
        }
    }
    while (morelist) {
        morelist = stripgarbage(morelist);
        event = morelist;
        tool = event->tool;
        if (tool->toolmaster->processlist) {
            event = processlist(tool,event);
        }
        else event = padeditlist(morelist);
        event = stripgarbage(event);
        morelist = 0;
        for (;event;) {
            next = event->next;
            event->next = 0;
            if (event->tool == &endtool) {
                event->tool = 0;
                event->next = donelist;
                donelist = event;
            }
            else {
                event->next = morelist;
                morelist = event;
            }
            event = next;
        }
    }
    for (event = donelist;event;event = event->next)
        event->type &= ~EVENT_PADEDIT;
    tool->next = 0;
    return((struct Event *)sorteventlist(donelist));
}

void editwithtool(edit,event,display)

struct Edit *edit;
struct Event *event;
short display;

{
    struct Tool *tool;
    struct Event *next;
    unsigned long time;
    if ((event->type != EVENT_VOICE) || (event->status == MIDI_NOTEOFF)) return;
    tool = functions.edittools[edit->toolid];
    if (!tool) return;
    assigncliptotool((struct MacroTool *)tool,&edit->clip);
    assigntracktotool((struct MacroTool *)tool,edit->track);
    functions.padcutin = edit->begin;
    functions.padcutout = edit->end;
    if (removenote(edit,(struct NoteEvent *)event)) {
        if (display) displayeventtype(edit,D_DELETE);
        event = padeditevent(event,tool);
        if (event) time = event->time;
        for (;event;) {
            next = event->next;
            edit->selectnote = (struct NoteEvent *) event;
            if (display) {
                displayeventtype(edit,D_HIGHLIGHT);
                displayedittime(edit,event->time);
                sendonenoteout(edit,(struct NoteEvent *)event,(event->time - time)
                    + functions.timenow);
            }
            clearmarkbits(event);
//          tagnote(edit,event);
            event->next = (struct Event *) edit->fillsequence;
            edit->fillsequence = (struct NoteEvent *) event;
            event = next;
        }
    }
}

void editregionwithtool(edit)

struct Edit *edit;

{
    struct Event *event, *next, *inlist, *outlist;
    long begin, end;
    struct Tool *tool;
    tool = functions.edittools[edit->toolid];
    if (!tool) return;
    begin = edit->begin;
    end = edit->end;
    edit->fillsequence = 0;
    assigncliptotool((struct MacroTool *)tool,&edit->clip);
    assigntracktotool((struct MacroTool *)tool,edit->track);
    functions.padcutin = begin;
    functions.padcutout = end;
    event = edit->clip.events.first;
    inlist = 0;
    outlist = 0;
    for (;event;) {
        next = event->next;
        if ((event->time < end) && (event->time >= begin)) {
            event->next = inlist;
            inlist = event;
        }
        else {
            event->next = outlist;
            outlist = event;
        }
        event = next;
    }
    inlist = padeditevent(inlist,tool);
    inlist = (struct Event *) List_Cat((struct ListItem *)inlist,(struct ListItem *)outlist);
    edit->clip.events.first = (struct Event *) sorteventlist(inlist);
    edit->clip.events.point = (struct Event *) edit->clip.events.first;
}

