#define PSMR_FLAT                       -8
#define PSMR_SHARP                      -7
#define PSMR_TIE                        -6
#define PSMR_REST                       -5
#define PSMR_INS_NOTE           -4
#define PSMR_DELETE_NOTE        -3
#define PSMR_SET_WORD           -2

F64 PopUpDuration()
{
        I64   i;
        CDoc *doc = DocNew;

        DocPrint(doc,
                                "$GREEN$$MU,\"Set Word\",LE=PSMR_SET_WORD$\n"
                                "$MU,\"Toggle Sharp\",LE=PSMR_SHARP$\n"
                                "$MU,\"Toggle Flat\",LE=PSMR_FLAT$\n"
                                "$MU,\"Toggle Tie\",LE=PSMR_TIE$\n"
                                "$MU,\"Make Rest\",LE=PSMR_REST$\n"
                                "$MU,\"Insert Note\",LE=PSMR_INS_NOTE$\n"
                                "$MU,\"Delete Note\",LE=PSMR_DELETE_NOTE$\n\n");
        for (i = 0; i < PSM_DURATIONS_NUM; i++)
                DocPrint(doc, "$MU,\"%7.5f\",LE=%d$\n", psm_durations[i], i);
        DocPrint(doc, "\n$MU,\"CANCEL\",LE=DOCM_CANCEL$\n");
        i = PopUpMenu(doc);
        DocDel(doc);

        return i;
}

U0 PsmRightClick(I64 x, I64 y)
{
        U8              *st, *st2;
        PsmNote *tmpn, *tmpn1;
        I64              i, old_doc_flags;

        if (DocPut)
                old_doc_flags = DocPut->flags;
        psm.cur_note = tmpn = PsmFindNote(x, y);
        if (tmpn != &psm.head)
        {
                Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
                i = PopUpDuration;
                if (0 <= i < PSM_DURATIONS_NUM)
                {
                        if (tmpn->type == PSMT_NOTE)
                                tmpn->duration=i;
                }
                else
                {
                        switch (i)
                        {
                                case PSMR_REST:
                                        if (tmpn->type == PSMT_NOTE)
                                                tmpn->ona = 0;
                                        break;

                                case PSMR_SHARP:
                                        if (tmpn->type == PSMT_NOTE && tmpn->ona)
                                        {
                                                if (Btr(&tmpn->flags, PSMf_FLAT))
                                                        tmpn->ona++;
                                                if (Btc(&tmpn->flags, PSMf_SHARP))
                                                        tmpn->ona--;
                                                else
                                                        tmpn->ona++;
                                        }
                                        break;

                                case PSMR_FLAT:
                                        if (tmpn->type == PSMT_NOTE && tmpn->ona)
                                        {
                                                if (Btr(&tmpn->flags, PSMf_SHARP))
                                                        tmpn->ona--;
                                                if (Btc(&tmpn->flags, PSMf_FLAT))
                                                        tmpn->ona++;
                                                else
                                                        tmpn->ona--;
                                        }
                                        break;

                                case PSMR_TIE:
                                        if (tmpn->type == PSMT_NOTE)
                                                Btc(&tmpn->flags, PSMf_TIE);
                                        break;

                                case PSMR_SET_WORD:
                                        if (tmpn->type == PSMT_NOTE)
                                        {
                                                if (DocPut)
                                                        DocPut->flags &= ~DOCF_FORM;
                                                if (PsmHasWords(tmpn->word))
                                                        st2 = MStrPrint("\nWord(\"%Q\"):", tmpn->word);
                                                else
                                                        st2 = MStrPrint("\nWord(\"\"):");
                                                DocBottom;
                                                st = StrGet(st2);
                                                Free(st2);
                                                Free(tmpn->word);
                                                if (*st)
                                                {
                                                        tmpn->word = MStrPrint("%q", st);
                                                        Free(st);
                                                }
                                                else
                                                        tmpn->word = StrNew(" ");
                                                if (DocPut)
                                                        DocPut->flags = DocPut->flags & ~DOCF_FORM | old_doc_flags & DOCF_FORM;
                                        }
                                        break;

                                case PSMR_INS_NOTE:
                                        tmpn1 = PsmNoteCopy(tmpn);
                                        QueueInsert(tmpn1, tmpn);
                                        break;

                                case PSMR_DELETE_NOTE:
                                        psm.cur_note = tmpn->next;
                                        QueueRemove(tmpn);
                                        PsmNoteDel(tmpn);
                                        break;
                        }
                }
                PsmSetWidth(psm.cur_note);
                Fs->win_inhibit = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_FOCUS_TASK_MENU - WIF_SELF_CTRLS;
        }
}

U0 PsmLeftClickPickNoteBox(I64 duration)
{
        I64              o, n, message_code, arg1, arg2;
        PsmNote *tmpn, *tmpn1;

        do
        {
                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_MOVE);
                if (message_code == MESSAGE_MS_MOVE)
                {
                        DrawDC2;
                        DrawNote(psm.dc2, arg1, arg2, duration);
                }
        }
        while (message_code != MESSAGE_MS_L_UP);

        if (arg2 < 13 * FONT_HEIGHT)
        {
                if (arg1 > psm.head.last->x)
                        tmpn1 = psm.head.last;
                else if (arg1 < psm.head.next->x)
                        tmpn1 = &psm.head;
                else
                        tmpn1 = PsmFindNote(arg1 - PSM_NOTE_SPACING / 2, arg2);
                tmpn = CAlloc(sizeof(PsmNote));
                tmpn->type = PSMT_NOTE;
                arg2 = arg2 / 4 - 15;
                n = arg2 % 7;
                o = 4 + arg2 / -7;
                if (n < 0)
                {
                        n += 7;
                        o++;
                }
                n = psm_note_inverse_map[n];
                if (n < 3)
                        o--;
                tmpn->ona               = Note2Ona(n, o);
                tmpn->duration  = duration;
                PsmSetWidth(tmpn);
                QueueInsert(tmpn, tmpn1);
                psm.cur_note = tmpn->next;
        }
        DrawDC2;
}

U0 PsmLeftClickPickMeterBox(I64 top, I64 bottom)
{
        I64              message_code, arg1, arg2;
        PsmNote *tmpn, *tmpn1;

        do
        {
                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_MOVE);
                if (message_code == MESSAGE_MS_MOVE)
                {
                        DrawDC2;
                        DrawTimeSignature(psm.dc2, arg1, arg2, top, bottom);
                }
        }
        while (message_code != MESSAGE_MS_L_UP);

        if (arg2 < 13 * FONT_HEIGHT)
        {
                if (arg1 >= psm.head.x)
                        tmpn1 = psm.head.last;
                else if (arg1 < psm.head.next->x)
                        tmpn1 = &psm.head;
                else
                        tmpn1 = PsmFindNote(arg1 - PSM_NOTE_SPACING / 2, arg2);
                tmpn = CAlloc(sizeof(PsmNote));
                tmpn->type                      = PSMT_METER;
                tmpn->meter_top         = top;
                tmpn->meter_bottom      = bottom;
                PsmSetWidth(tmpn);
                QueueInsert(tmpn, tmpn1);
                psm.cur_note = tmpn->next;
        }
        DrawDC2;
}

U0 PsmLeftClickStaffPtr(I64 x, I64 y)
{
        PsmNote *tmpn, *tmpn1;
        I64              o, n, message_code, arg1, arg2, n_original, o_original;

        psm.cur_note = tmpn = PsmFindNote(x, y);
        if (tmpn != &psm.head)
        {
                if (tmpn->type == PSMT_NOTE)
                {
                        o_original = Ona2Octave(tmpn->ona);
                        n_original = Ona2Note(tmpn->ona);
                        do
                        {
                                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_MOVE);
                                if (message_code == MESSAGE_MS_L_UP)
                                {
                                        tmpn1 = PsmFindNote(arg1, arg2);
                                        if (tmpn1 == &psm.head || tmpn1 == tmpn)
                                                goto move_note;
                                        else
                                        {
                                                Free(tmpn1->word);
                                                tmpn1->word = tmpn->word;
                                                tmpn->word      = NULL;
                                                tmpn->ona       = Note2Ona(n_original, o_original);
                                        }
                                }
                                else
                                {
move_note:
                                        arg2 = arg2 / 4 - 15;
                                        n = arg2 % 7;
                                        o = 4 + arg2 / -7;
                                        if (n < 0)
                                        {
                                                n += 7;
                                                o++;
                                        }
                                        n = psm_note_inverse_map[n];
                                        if (n < 3)
                                                o--;
                                        tmpn->ona = Note2Ona(n, o);
                                }
                        }
                        while (message_code != MESSAGE_MS_L_UP);

                        PsmSetWidth(tmpn);
                }
        }
}

U0 PsmLeftClickStaffBox(I64 x, I64 y)
{
        I64 message_code, arg1, arg2;

        do
        {
                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_MOVE);
                DrawDC2;
                psm.dc2->color = ROPF_DITHER + WHITE << 16 + BLACK;
                GrBorder(psm.dc2, x, y, arg1, arg2);
                if (message_code == MESSAGE_MS_L_UP)
                {
                        if (x > arg1)
                                SwapI64(&x, &arg1);
                        PsmMarkSel(x, arg1, TRUE);
                }
        }
        while (message_code != MESSAGE_MS_L_UP);

        DrawDC2;
}

U0 PsmLeftClick(I64 x, I64 y)
{
        I64 duration, top, bottom;

        if (y < 13 * FONT_HEIGHT)
        {
                if (psm.tool == PSMTT_PTR_TOOL)
                        PsmLeftClickStaffPtr(x, y);
                else
                        PsmLeftClickStaffBox(x, y);
        }
        else
        {
                duration = PsmGetPickNoteBoxDuration(x, y);
                if (0 <= duration < PSM_DURATIONS_NUM)
                        PsmLeftClickPickNoteBox(duration);

                else if (PsmGetPickMeterBox(x, y, &top, &bottom))
                        PsmLeftClickPickMeterBox(top, bottom);

                else if (PsmGetPickToolBox(x, y))
                        DrawDC2;
        }
}

U8 PsmConvertDuration(F64 d)
{
        F64 d1, d2;
        I64 j;

        for (j = 0; j < PSM_DURATIONS_NUM; j++)
        {
                d1 = psm_durations[j];
                d2 = psm_durations[j + 1];
                if (d < d1 * d2 / (d1 + d2))
                        return j;
        }

        return 0;
}

#define PSM_KEYS_NUM            20
class PsmKey
{
        U8 x, w, h, ascii;
};

#define PSM_W_W         16
#define PSM_W_H         36
#define PSM_B_W         8
#define PSM_B_H         20

PsmKey psm_kbd[PSM_KEYS_NUM] = {
        { 2 * PSM_W_W - 4, PSM_B_W, PSM_B_H, 'e' }, 
        { 3 * PSM_W_W - 4, PSM_B_W, PSM_B_H, 'r' }, 
        { 4 * PSM_W_W - 4, PSM_B_W, PSM_B_H, 't' }, 
        { 6 * PSM_W_W - 4, PSM_B_W, PSM_B_H, 'u' }, 
        { 7 * PSM_W_W - 4, PSM_B_W, PSM_B_H, 'i' }, 
        { 9 * PSM_W_W - 4, PSM_B_W, PSM_B_H, 'p' }, 
        {10 * PSM_W_W - 4, PSM_B_W, PSM_B_H, '[' }, 
        {11 * PSM_W_W - 4, PSM_B_W, PSM_B_H, ']' }, 

        { 0 * PSM_W_W, PSM_W_W, PSM_W_H, 'a' }, 
        { 1 * PSM_W_W, PSM_W_W, PSM_W_H, 's' }, 
        { 2 * PSM_W_W, PSM_W_W, PSM_W_H, 'd' }, 
        { 3 * PSM_W_W, PSM_W_W, PSM_W_H, 'f' }, 
        { 4 * PSM_W_W, PSM_W_W, PSM_W_H, 'g' }, 
        { 5 * PSM_W_W, PSM_W_W, PSM_W_H, 'h' }, 
        { 6 * PSM_W_W, PSM_W_W, PSM_W_H, 'j' }, 
        { 7 * PSM_W_W, PSM_W_W, PSM_W_H, 'k' }, 
        { 8 * PSM_W_W, PSM_W_W, PSM_W_H, 'l' }, 
        { 9 * PSM_W_W, PSM_W_W, PSM_W_H, ';' }, 
        {10 * PSM_W_W, PSM_W_W, PSM_W_H, '\'' }, 
        {11 * PSM_W_W, PSM_W_W, PSM_W_H, '\n'}, 
};

U0 PsmDownKey(I64 x, I64 y)
{
        I64             i;
        PsmKey *o;

        y -= FONT_HEIGHT * 13;
        if (0 <= y < PSM_W_H)
        {
                x -= 16;
                for (i = 0; i < PSM_KEYS_NUM; i++)
                {
                        o=&psm_kbd[i];
                        if (o->x <= x < o->x+o->w && y < o->h)
                        {
                                Message(MESSAGE_KEY_DOWN, o->ascii, 0);
                                return;
                        }
                }
        }
}

U0 PsmUpKey(I64 x, I64 y)
{
        I64             i;
        PsmKey *o;

        y -= FONT_HEIGHT * 13;
        if (0 <= y < PSM_W_H)
        {
                x -= 16;
                for (i = 0; i < PSM_KEYS_NUM; i++)
                {
                        o=&psm_kbd[i];
                        if (o->x <= x < o->x+o->w && y < o->h)
                        {
                                Message(MESSAGE_KEY_UP, o->ascii, 0);
                                return;
                        }
                }
        }
}

U0 PsmPushMode(I64 psm_octave)
{
        Fs->win_inhibit = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_FOCUS_TASK_MENU - WIF_SELF_CTRLS;
        PsmMenu(psm_octave);
}

U0 PsmPopMode()
{
        Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
        DCFill;
}

#define PSMF_CD                         1
#define PSMF_INCOMPLETE         2

U0 Psalmody(U8 *dirname="~/Psalmody")
{
        Bool     was_playing, is_null = TRUE, was_null = TRUE;
        I64              arg1, arg2, message_code = 0, col, ona = 0, last_ona = 0, 
                         psm_octave = 4, timeout_val, timeout_val2, old_doc_flags;
        U8              *filename = NULL, *st, *st2;
        PsmNote *tmpn;
        F64              psm_duration = 1.0, d, event_time = tS, note_down_time = tS;
        CCtrl   *c = TempoNew;

        if (DocPut)
                old_doc_flags = DocPut->flags;
        SettingsPush; //See SettingsPush

        MusicSettingsReset;
        tempo_state.tempo        = Round(TEMPO_RANGE * (music.tempo - 0.5) / 4.4);
        tempo_state.stacatto = Round(TEMPO_RANGE * (music.stacatto_factor - 0.12) / 0.88);

        if (DocPut)
                DocPut->flags |= DOCF_FORM;

        MemSet(&psm, 0, sizeof(PsmCtrl));
        psm.screen_x = 0;
        psm.head.next = psm.head.last = &psm.head;
        psm.clip.next = psm.clip.last = &psm.clip;
        psm.cur_note = &psm.head;
        psm.dc2 = DCAlias;

        MenuPush(       "File {"
                                "  New(,'.');"
                                "  ChangeDir(MESSAGE_CMD,PSMF_CD);"
                                "  Open(,CH_CTRLO);"
                                "  SaveAs(,CH_CTRLA);"
                                "  Abort(,CH_SHIFT_ESC);"
                                "  Exit(,CH_ESC);"
                                "}"
                                "Edit {"
                                "  Cut(,CH_CTRLX);"
                                "  Copy(,CH_CTRLC);"
                                "  Paste(,CH_CTRLV);"
                                "  RightMenu(,'\n');"
                                "  BackSpace(,CH_BACKSPACE);"
                                "  DeleteNote(,,SC_DELETE);"
                                "  ClearSong(,'.');"
                                "  Left(,,SC_CURSOR_LEFT);"
                                "  Right(,,SC_CURSOR_RIGHT);"
                                "  GoBegin(,,0x4CB0000044B);"
                                "  GoEnd(,,0x4CD0000044D);"
                                "}"
                                "Song {"
                                "  Play(,'x');"
                                "  Record(,'z');"
                                "  Random(,',');"
                                "  MarkIncomplete(MESSAGE_CMD,PSMF_INCOMPLETE);"
                                "}"
                                "Sound {"
                                "  Octave1(,'1');"
                                "  Octave2(,'2');"
                                "  Octave3(,'3');"
                                "  Octave4(,'4');"
                                "  Octave5(,'5');"
                                "  Octave6(,'6');"
                                "  Octave7(,'7');"
                                "}"
                                "Help {"
                                "  Help(,,SC_F1);"
                                "}"
                                );
        psm.incomplete_entry = MenuEntryFind(Fs->cur_menu, "Song/MarkIncomplete");
        psm.record_entry         = MenuEntryFind(Fs->cur_menu, "Song/Record");

        AutoComplete;
        WinBorder;
        WinMax;

        dirname = StrNew(dirname);
        PsmPushMode(psm_octave);
        col = 0;
        Fs->draw_it = &DrawIt;

        try
        {
                while (TRUE)
                {
                        was_playing=FALSE;
mo_start:
                        if (mouse.pos_text.y - Fs->win_top < 18)
                                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN   | 1 << MESSAGE_KEY_UP   |
                                                                                                                1 << MESSAGE_MS_L_DOWN  | 1 << MESSAGE_MS_L_UP  |
                                                                                                                1 << MESSAGE_MS_R_UP    | 1 << MESSAGE_MS_MOVE  | 1 << MESSAGE_CMD);
                        else
                                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN   | 1 << MESSAGE_KEY_UP |
                                                                                                                1 << MESSAGE_MS_MOVE    | 1 << MESSAGE_CMD);
mo_got_message:
                        if (message_code == MESSAGE_KEY_DOWN && arg1 == CH_SPACE && !arg2)
                        {
                                //The Window Mgr sets the Doc cur_entry to a bttn
                                //and generates a <SPACE> when the Doc Bttns are clicked.
                                //This is so that kbd and mouse are the same for Doc's.
                                //We must now pass the <SPACE> onto the Doc handler.
                                PutKey(arg1, arg2);
                                goto mo_start;
                        }
                        if (message_code != MESSAGE_MS_MOVE)
                        {
                                DocBottom;
                                if (was_playing || DocPut->cur_entry->y >= Fs->win_height - 2)
                                {
                                        PsmMenu(psm_octave);
                                        col = 0;
                                }
                        }

                        ona = Note2Ona(3, psm_octave + 1); //C
                        is_null = TRUE;
                        switch (message_code)
                        {
                                case MESSAGE_CMD:
                                        PsmPopMode;
                                        switch (arg1)
                                        {
                                                case PSMF_CD:
                                                        st2 = dirname;
                                                        if (dirname = PopUpPickDir)
                                                        {
                                                                Free(st2);
                                                                Free(filename);
                                                                filename = NULL;
                                                        }
                                                        else
                                                                dirname = st2;
                                                        break;

                                                case PSMF_INCOMPLETE:
                                                        psm.incomplete_entry->checked = !psm.incomplete_entry->checked;
                                                        break;
                                        }
                                        PsmPushMode(psm_octave);
                                        col = 0;
                                        break;

                                case MESSAGE_KEY_DOWN:
                                        event_time = tS;
                                        if ('0' <= arg1 <= '9')
                                        {
                                                psm_octave = arg1 - '0';
                                                PsmMenu(psm_octave);
                                                col = 0;
                                        }
                                        else
                                        {
                                                switch (arg1)
                                                {
                                                        start:
                                                                case 'a':       ona -= 8;               break;
                                                                case 's':       ona -= 7;               break;
                                                                case 'e':       ona -= 6;               break;
                                                                case 'd':       ona -= 5;               break;
                                                                case 'r':       ona -= 4;               break;
                                                                case 'f':       ona -= 3;               break;
                                                                case 't':       ona -= 2;               break;
                                                                case 'g':       ona--;                  break;
                                                                case 'h':                                       break;
                                                                case 'u':       ona++;                  break;
                                                                case 'j':       ona += 2;               break;
                                                                case 'i':       ona += 3;               break;
                                                                case 'k':       ona += 4;               break;
                                                                case 'l':       ona += 5;               break;
                                                                case 'p':       ona += 6;               break;
                                                                case ';':       ona += 7;               break;
                                                                case '[':       ona += 8;               break;
                                                                case '\'':      ona += 9;               break;
                                                                case ']':       ona += 10;              break;
                                                                case CH_SPACE:  ona = 0;        break;
                                                        end:
                                                                is_null = FALSE;
                                                                break;

                                                        case 0:
                                                                switch (arg2.u8[0])
                                                                {
                                                                        case SC_CURSOR_LEFT:
                                                                                if (arg2 & SCF_CTRL)
                                                                                {
                                                                                        while (psm.cur_note->last != &psm.head)
                                                                                        {
                                                                                                psm.cur_note = psm.cur_note->last;
                                                                                                if (psm.cur_note != &psm.head)
                                                                                                        LBEqual(&psm.cur_note->flags, PSMf_SEL, arg2 & SCF_SHIFT);
                                                                                        }
                                                                                }
                                                                                else
                                                                                {
                                                                                        if (psm.cur_note->last != &psm.head)
                                                                                        {
                                                                                                psm.cur_note = psm.cur_note->last;
                                                                                                if (psm.cur_note != &psm.head)
                                                                                                        LBEqual(&psm.cur_note->flags, PSMf_SEL, arg2 & SCF_SHIFT);
                                                                                        }
                                                                                }
                                                                                break;

                                                                        case SC_CURSOR_RIGHT:
                                                                                if (arg2 & SCF_CTRL)
                                                                                {
                                                                                        while (psm.cur_note != &psm.head)
                                                                                        {
                                                                                                if (psm.cur_note != &psm.head)
                                                                                                        LBEqual(&psm.cur_note->flags, PSMf_SEL, arg2 & SCF_SHIFT);
                                                                                                psm.cur_note = psm.cur_note->next;
                                                                                        }
                                                                                }
                                                                                else
                                                                                {
                                                                                        if (psm.cur_note != &psm.head)
                                                                                        {
                                                                                                if (psm.cur_note != &psm.head)
                                                                                                        LBEqual(&psm.cur_note->flags, PSMf_SEL, arg2 & SCF_SHIFT);
                                                                                                psm.cur_note = psm.cur_note->next;
                                                                                        }
                                                                                }
                                                                                break;

                                                                        case SC_DELETE:
                                                                                if (arg2 & SCF_SHIFT)
                                                                                        PsmCutToClip;
                                                                                else
                                                                                {
                                                                                        tmpn = psm.cur_note;
                                                                                        psm.cur_note = tmpn->next;
                                                                                        if (tmpn != &psm.head)
                                                                                        {
                                                                                                QueueRemove(tmpn);
                                                                                                PsmNoteDel(tmpn);
                                                                                        }
                                                                                }
                                                                                break;

                                                                        case SC_INS:
                                                                                if (arg2 & SCF_SHIFT)
                                                                                        PsmPasteClip;
                                                                                else if (arg2  &SCF_CTRL)
                                                                                        PsmCopyToClip;
                                                                                break;

                                                                        case SC_F1:
                                                                                PsmPopMode;
                                                                                PopUpEd("::/Apps/Psalmody/Help.DD", Fs);
                                                                                PsmPushMode(psm_octave);
                                                                                col = 0;
                                                                                break;
                                                                }
                                                                break;
                                                        case ',':
                                                                Free(filename);
                                                                filename = NULL;
                                                                PsmPopMode;
                                                                music.octave = psm_octave;
                                                                if (st2 = GodSongStr) {
                                                                        PsmLoadSongStr(st2, &psm_octave, &psm_duration);
                                                                        Free(st2);
                                                                }
                                                                PsmPushMode(psm_octave);
                                                                col = 0;
                                                                break;

                                                        case CH_CTRLO:
                                                                PsmPopMode;
                                                                RegOneTimePopUp(ARf_PSALMODY_JUKEBOX,
                                                                                        "Sel a song and preview it.\n"
                                                                                        "$GREEN$<SHIFT-ESC>$FG$ to load it into Psalmody.\n\n"
                                                                                        ST_WARN_ST " Graphics and other embelishments\n"
                                                                                        "will be lost because Psalmody can't\n"
                                                                                        "parse CosmiC programs completely.\n");
                                                                Free(filename);
                                                                filename = NULL;
                                                                JukeBox(dirname, &filename);
                                                                if (filename)
                                                                {
                                                                        psm.screen_x = 0;
                                                                        psm_duration = 1.0;
                                                                        psm_octave       = 4;
                                                                        PsmSongDel(&psm.head);
                                                                        psm.cur_note = &psm.head;
                                                                        PsmLoadSong(filename, &psm_octave, &psm_duration);
                                                                        psm.record_entry->checked = FALSE;
                                                                        psm.cur_note = psm.head.next;
                                                                }
                                                                PsmPushMode(psm_octave);
                                                                col = 0;
                                                                break;

                                                        case CH_CTRLA:
                                                                PsmPopMode;
                                                                filename = PsmSaveSong(dirname, filename);
                                                                PsmPushMode(psm_octave);
                                                                break;

                                                        case CH_CTRLC:
                                                                PsmCopyToClip;
                                                                break;

                                                        case CH_CTRLV:
                                                                PsmPasteClip;
                                                                break;

                                                        case CH_CTRLX:
                                                                PsmCutToClip;
                                                                break;

                                                        case '.':
                                                                PsmMenu(psm_octave);
                                                                col = 0;
                                                                Free(filename);
                                                                filename = NULL;
                                                                psm_duration = 1.0;
                                                                psm_octave       = 4;
                                                                PsmSongDel(&psm.head);
                                                                psm.cur_note = &psm.head;
                                                                psm.screen_x = 0;
                                                                break;

                                                        case '\n':
                                                                if (psm.cur_note != &psm.head)
                                                                        PsmRightClick(psm.cur_note->x, psm.cur_note->y);
                                                                break;

                                                        case 'x':
                                                                if (was_playing)
                                                                        break;
                                                                col = 0;
                                                                psm.playing = TRUE;
                                                                PsmMenu(psm_octave);
                                                                tmpn = psm.cur_note;
                                                                while (tmpn != &psm.head)
                                                                {
                                                                        if (tmpn->type != PSMT_METER)
                                                                        {
                                                                                timeout_val =counts.jiffies;
                                                                                if (mouse.pos_text.y - Fs->win_top < 18)
                                                                                        message_code = MessageScan(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN |
                                                                                                                        1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_R_UP | 1 << MESSAGE_CMD);
                                                                                else
                                                                                        message_code = MessageScan(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN |
                                                                                                                        1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_CMD);
                                                                                if (message_code)
                                                                                {
                                                                                        Sound;
                                                                                        psm.playing = FALSE;
                                                                                        was_playing = TRUE;
                                                                                        if (mouse.pos_text.y - Fs->win_top >= 18 && message_code == MESSAGE_MS_L_DOWN)
                                                                                                goto mo_start;
                                                                                        else
                                                                                                goto mo_got_message;
                                                                                }
                                                                                psm.cur_note = tmpn;
                                                                                psm.screen_x += tmpn->x - 0.33 * GR_WIDTH;
                                                                                if (PsmHasWords(tmpn->word))
                                                                                        "%s", tmpn->word;
                                                                                Sound(tmpn->ona);

                                                                                music.tempo                             = 4.4  * tempo_state.tempo / TEMPO_RANGE + 0.5;
                                                                                music.stacatto_factor   = 0.88 * tempo_state.stacatto / TEMPO_RANGE + 0.12;
                                                                                d = JIFFY_FREQ * psm_durations[tmpn->duration] / music.tempo;
                                                                                if (Bt(&tmpn->flags, PSMf_TIE))
                                                                                {
                                                                                        timeout_val += d;
                                                                                        timeout_val2 = timeout_val;
                                                                                }
                                                                                else
                                                                                {
                                                                                        timeout_val += d * music.stacatto_factor;
                                                                                        timeout_val2 = timeout_val + d * (1.0 - music.stacatto_factor);
                                                                                }
                                                                                SleepUntil(timeout_val);
                                                                                Sound;
                                                                                SleepUntil(timeout_val2);
                                                                        }
                                                                        tmpn = tmpn->next;
                                                                }
                                                                psm.cur_note = &psm.head;
                                                                psm.screen_x += psm.cur_note->x - GR_WIDTH / 2;
                                                                psm.playing = FALSE;
                                                                PsmMenu(psm_octave);
                                                                col = 0;
                                                                Sound;
                                                                break;

                                                        case CH_BACKSPACE:
                                                                tmpn=psm.cur_note->last;
                                                                if (tmpn != &psm.head)
                                                                {
                                                                        QueueRemove(tmpn);
                                                                        PsmNoteDel(tmpn);
                                                                }
                                                                if (col)
                                                                {
                                                                        '' CH_BACKSPACE;
                                                                        col--;
                                                                }
                                                                break;

                                                        case 'z':
                                                                if (psm.record_entry->checked)
                                                                        psm.record_entry->checked = FALSE;
                                                                else
                                                                {
                                                                        psm.record_entry->checked = TRUE;
                                                                        psm_duration    = 1.0;
                                                                        psm_octave              = 4;
                                                                        psm.screen_x    = 0;
                                                                }
                                                                PsmMenu(psm_octave);
                                                                col = 0;
                                                                break;

                                                        case CH_ESC:
                                                                PsmPopMode;
                                                                filename = PsmSaveSong(dirname, filename);
                                                                PsmPushMode(psm_octave);

                                                        case CH_SHIFT_ESC:
                                                                goto mo_done;
                                                }
                                        }
                                        break;

                                case MESSAGE_KEY_UP:
                                        event_time = tS;
                                        break;

                                case MESSAGE_MS_MOVE:
                                        if (arg2 > 18 * FONT_HEIGHT)
                                                Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
                                        else
                                                Fs->win_inhibit = WIG_TASK_DEFAULT -
                                                                                        WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_FOCUS_TASK_MENU - WIF_SELF_CTRLS;
                                        break;

                                case MESSAGE_MS_L_DOWN:
                                        PsmDownKey(arg1, arg2);
                                        PsmLeftClick(arg1, arg2);
                                        break;

                                case MESSAGE_MS_L_UP:
                                        PsmUpKey(arg1, arg2);
                                        break;

                                default:
                                        PsmRightClick(arg1, arg2);
                        }
                        if (is_null)
                                ona = 0;
                        if (ona != last_ona || is_null != was_null)
                        {
                                if (!ona)
                                {
                                        if (is_null)
                                                st = "";
                                        else
                                                st = "R";
                                }
                                else
                                        st = ListSub(Ona2Note(ona), psm_note_list);
                                Sound(ona);
                                if (psm.record_entry->checked)
                                {
                                        if (!was_null)
                                        {
                                                music.tempo = 4.4 * tempo_state.tempo / TEMPO_RANGE + 0.5;
                                                music.stacatto_factor = 0.88 * tempo_state.stacatto / TEMPO_RANGE + 0.12;
                                                tmpn->duration = PsmConvertDuration(music.tempo * (event_time-note_down_time));
                                                PsmSetWidth(tmpn);
                                                QueueInsert(tmpn, psm.cur_note->last);
                                        }
                                        if (!is_null)
                                        {
                                                note_down_time = tS;
                                                tmpn = CAlloc(sizeof(PsmNote));
                                                tmpn->type = PSMT_NOTE;
                                                tmpn->ona = ona;
                                                if (st[1] == '#')
                                                        Bts(&tmpn->flags, PSMf_SHARP);
                                        }
                                }
                                last_ona = ona;
                                was_null = is_null;
                                "%s", st;
                                col += StrLen(st);
                                if (col >= Fs->win_width-1)
                                {
                                        '\n';
                                        col = 0;
                                }
                        }
                }
mo_done:
                MessageGet(,, 1 << MESSAGE_KEY_UP);
        }
        catch
                PutExcept;
        PsmPopMode;
        PsmSongDel(&psm.head);
        PsmSongDel(&psm.clip);
        TempoDel(c);
        DCFill;
        DCDel(psm.dc2);
        DocClear;
        SettingsPop;
        if (DocPut)
                DocPut->flags = DocPut->flags & ~DOCF_FORM | old_doc_flags & DOCF_FORM;
        Free(dirname);
        MenuPop;
}