#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 $LK,"SettingsPush",A="MN: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;
}
