ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_pattern_ed.c (61777B)


      1 // for finding memory leaks in debug mode with Visual Studio
      2 #if defined _DEBUG && defined _MSC_VER
      3 #include <crtdbg.h>
      4 #endif
      5 
      6 #include <stdio.h>
      7 #include <stdint.h>
      8 #include <stdbool.h>
      9 #include "ft2_header.h"
     10 #include "ft2_config.h"
     11 #include "ft2_pattern_ed.h"
     12 #include "ft2_gui.h"
     13 #include "ft2_sample_ed.h"
     14 #include "ft2_pattern_draw.h"
     15 #include "ft2_inst_ed.h"
     16 #include "scopes/ft2_scopes.h"
     17 #include "ft2_diskop.h"
     18 #include "ft2_audio.h"
     19 #include "ft2_wav_renderer.h"
     20 #include "ft2_mouse.h"
     21 #include "ft2_video.h"
     22 #include "ft2_tables.h"
     23 #include "ft2_bmp.h"
     24 #include "ft2_structs.h"
     25 
     26 // for pattern marking w/ keyboard
     27 static int8_t lastChMark;
     28 static int16_t lastRowMark;
     29 
     30 // for pattern marking w/ mouse
     31 static int32_t lastMarkX1 = -1, lastMarkX2 = -1, lastMarkY1 = -1, lastMarkY2 = -1;
     32 
     33 static const uint8_t ptnNumRows[8] = { 27, 25, 20, 19, 42, 40, 31, 30 };
     34 static const uint8_t ptnLineSub[8] = { 13, 12,  9,  9, 20, 19, 15, 14 };
     35 static const uint8_t iSwitchExtW[4] = { 40, 40, 40, 39 };
     36 static const uint8_t iSwitchExtY[8] = { 2, 2, 2, 2, 19, 19, 19, 19 };
     37 static const uint8_t iSwitchY[8] = { 2, 19, 36, 53, 73, 90, 107, 124 };
     38 static const uint16_t iSwitchExtX[4] = { 221, 262, 303, 344 };
     39 
     40 static int32_t lastMouseX, lastMouseY;
     41 static int32_t last_TimeH, last_TimeM, last_TimeS;
     42 
     43 static note_t tmpPattern[MAX_CHANNELS * MAX_PATT_LEN];
     44 
     45 volatile pattMark_t pattMark; // globalized
     46 
     47 bool allocatePattern(uint16_t pattNum) // for tracker use only, not in loader!
     48 {
     49 	const bool audioWasntLocked = !audio.locked;
     50 	if (audioWasntLocked)
     51 		lockAudio();
     52 
     53 	if (pattern[pattNum] == NULL)
     54 	{
     55 		/* Original FT2 allocates only the amount of rows needed, but we don't
     56 		** do that to avoid out of bondary row look-up between out-of-sync replayer
     57 		** state and tracker state (yes it used to happen, rarely). We're not wasting
     58 		** too much RAM for a modern computer anyway. Worst case: 256 allocated
     59 		** patterns would be ~10MB.
     60 		**/
     61 
     62 		pattern[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
     63 		if (pattern[pattNum] == NULL)
     64 		{
     65 			if (audioWasntLocked)
     66 				unlockAudio();
     67 
     68 			return false;
     69 		}
     70 
     71 		song.currNumRows = patternNumRows[pattNum];
     72 	}
     73 
     74 	if (audioWasntLocked)
     75 		unlockAudio();
     76 
     77 	return true;
     78 }
     79 
     80 void killPatternIfUnused(uint16_t pattNum) // for tracker use only, not in loader!
     81 {
     82 	const bool audioWasntLocked = !audio.locked;
     83 	if (audioWasntLocked)
     84 		lockAudio();
     85 
     86 	if (patternEmpty(pattNum))
     87 	{
     88 		if (pattern[pattNum] != NULL)
     89 		{
     90 			free(pattern[pattNum]);
     91 			pattern[pattNum] = NULL;
     92 		}
     93 	}
     94 
     95 	if (audioWasntLocked)
     96 		unlockAudio();
     97 }
     98 
     99 uint8_t getMaxVisibleChannels(void)
    100 {
    101 	assert(config.ptnMaxChannels >= 0 && config.ptnMaxChannels <= 3);
    102 	if (config.ptnShowVolColumn)
    103 		return maxVisibleChans1[config.ptnMaxChannels];
    104 	else
    105 		return maxVisibleChans2[config.ptnMaxChannels];
    106 }
    107 
    108 void updatePatternWidth(void)
    109 {
    110 	if (ui.numChannelsShown > ui.maxVisibleChannels)
    111 		ui.numChannelsShown = ui.maxVisibleChannels;
    112 
    113 	assert(ui.numChannelsShown >= 2 && ui.numChannelsShown <= 12);
    114 
    115 	ui.patternChannelWidth = chanWidths[(ui.numChannelsShown / 2) - 1] + 3;
    116 }
    117 
    118 void updateAdvEdit(void)
    119 {
    120 	hexOutBg(92, 113, PAL_FORGRND, PAL_DESKTOP, editor.srcInstr, 2);
    121 	hexOutBg(92, 126, PAL_FORGRND, PAL_DESKTOP, editor.curInstr, 2);
    122 }
    123 
    124 void setAdvEditCheckBoxes(void)
    125 {
    126 	checkBoxes[CB_ENABLE_MASKING].checked = editor.copyMaskEnable;
    127 	checkBoxes[CB_COPY_MASK_0].checked = editor.copyMask[0];
    128 	checkBoxes[CB_COPY_MASK_1].checked = editor.copyMask[1];
    129 	checkBoxes[CB_COPY_MASK_2].checked = editor.copyMask[2];
    130 	checkBoxes[CB_COPY_MASK_3].checked = editor.copyMask[3];
    131 	checkBoxes[CB_COPY_MASK_4].checked = editor.copyMask[4];
    132 	checkBoxes[CB_PASTE_MASK_0].checked = editor.pasteMask[0];
    133 	checkBoxes[CB_PASTE_MASK_1].checked = editor.pasteMask[1];
    134 	checkBoxes[CB_PASTE_MASK_2].checked = editor.pasteMask[2];
    135 	checkBoxes[CB_PASTE_MASK_3].checked = editor.pasteMask[3];
    136 	checkBoxes[CB_PASTE_MASK_4].checked = editor.pasteMask[4];
    137 	checkBoxes[CB_TRANSP_MASK_0].checked = editor.transpMask[0];
    138 	checkBoxes[CB_TRANSP_MASK_1].checked = editor.transpMask[1];
    139 	checkBoxes[CB_TRANSP_MASK_2].checked = editor.transpMask[2];
    140 	checkBoxes[CB_TRANSP_MASK_3].checked = editor.transpMask[3];
    141 	checkBoxes[CB_TRANSP_MASK_4].checked = editor.transpMask[4];
    142 
    143 	showCheckBox(CB_ENABLE_MASKING);
    144 	showCheckBox(CB_COPY_MASK_0);
    145 	showCheckBox(CB_COPY_MASK_1);
    146 	showCheckBox(CB_COPY_MASK_2);
    147 	showCheckBox(CB_COPY_MASK_3);
    148 	showCheckBox(CB_COPY_MASK_4);
    149 	showCheckBox(CB_PASTE_MASK_0);
    150 	showCheckBox(CB_PASTE_MASK_1);
    151 	showCheckBox(CB_PASTE_MASK_2);
    152 	showCheckBox(CB_PASTE_MASK_3);
    153 	showCheckBox(CB_PASTE_MASK_4);
    154 	showCheckBox(CB_TRANSP_MASK_0);
    155 	showCheckBox(CB_TRANSP_MASK_1);
    156 	showCheckBox(CB_TRANSP_MASK_2);
    157 	showCheckBox(CB_TRANSP_MASK_3);
    158 	showCheckBox(CB_TRANSP_MASK_4);
    159 }
    160 
    161 void drawAdvEdit(void)
    162 {
    163 	drawFramework(  0,  92, 110,  17, FRAMEWORK_TYPE1);
    164 	drawFramework(  0, 109, 110,  64, FRAMEWORK_TYPE1);
    165 	drawFramework(110,  92, 124,  81, FRAMEWORK_TYPE1);
    166 	drawFramework(234,  92,  19,  81, FRAMEWORK_TYPE1);
    167 	drawFramework(253,  92,  19,  81, FRAMEWORK_TYPE1);
    168 	drawFramework(272,  92,  19,  81, FRAMEWORK_TYPE1);
    169 
    170 	textOutShadow(  4,  96, PAL_FORGRND, PAL_DSKTOP2, "Instr. remap:");
    171 	textOutShadow(  4, 113, PAL_FORGRND, PAL_DSKTOP2, "Old number");
    172 	textOutShadow(  4, 126, PAL_FORGRND, PAL_DSKTOP2, "New number");
    173 	textOutShadow(129,  96, PAL_FORGRND, PAL_DSKTOP2, "Masking enable");
    174 	textOutShadow(114, 109, PAL_FORGRND, PAL_DSKTOP2, "Note");
    175 	textOutShadow(114, 122, PAL_FORGRND, PAL_DSKTOP2, "Instrument number");
    176 	textOutShadow(114, 135, PAL_FORGRND, PAL_DSKTOP2, "Volume column");
    177 	textOutShadow(114, 148, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 1");
    178 	textOutShadow(114, 161, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 2,3");
    179 
    180 	charOutShadow(239, 95, PAL_FORGRND, PAL_DSKTOP2, 'C');
    181 	charOutShadow(258, 95, PAL_FORGRND, PAL_DSKTOP2, 'P');
    182 	charOutShadow(277, 95, PAL_FORGRND, PAL_DSKTOP2, 'T');
    183 
    184 	showPushButton(PB_REMAP_TRACK);
    185 	showPushButton(PB_REMAP_PATTERN);
    186 	showPushButton(PB_REMAP_SONG);
    187 	showPushButton(PB_REMAP_BLOCK);
    188 
    189 	setAdvEditCheckBoxes();
    190 
    191 	updateAdvEdit();
    192 }
    193 
    194 void hideAdvEdit(void)
    195 {
    196 	ui.advEditShown = false;
    197 
    198 	hidePushButton(PB_REMAP_TRACK);
    199 	hidePushButton(PB_REMAP_PATTERN);
    200 	hidePushButton(PB_REMAP_SONG);
    201 	hidePushButton(PB_REMAP_BLOCK);
    202 
    203 	hideCheckBox(CB_ENABLE_MASKING);
    204 	hideCheckBox(CB_COPY_MASK_0);
    205 	hideCheckBox(CB_COPY_MASK_1);
    206 	hideCheckBox(CB_COPY_MASK_2);
    207 	hideCheckBox(CB_COPY_MASK_3);
    208 	hideCheckBox(CB_COPY_MASK_4);
    209 	hideCheckBox(CB_PASTE_MASK_0);
    210 	hideCheckBox(CB_PASTE_MASK_1);
    211 	hideCheckBox(CB_PASTE_MASK_2);
    212 	hideCheckBox(CB_PASTE_MASK_3);
    213 	hideCheckBox(CB_PASTE_MASK_4);
    214 	hideCheckBox(CB_TRANSP_MASK_0);
    215 	hideCheckBox(CB_TRANSP_MASK_1);
    216 	hideCheckBox(CB_TRANSP_MASK_2);
    217 	hideCheckBox(CB_TRANSP_MASK_3);
    218 	hideCheckBox(CB_TRANSP_MASK_4);
    219 
    220 	ui.scopesShown = true;
    221 	drawScopeFramework();
    222 }
    223 
    224 void showAdvEdit(void)
    225 {
    226 	if (ui.extendedPatternEditor)
    227 		exitPatternEditorExtended();
    228 
    229 	hideTopScreen();
    230 	showTopScreen(false);
    231 
    232 	ui.advEditShown = true;
    233 	ui.scopesShown  = false;
    234 	drawAdvEdit();
    235 }
    236 
    237 void toggleAdvEdit(void)
    238 {
    239 	if (ui.advEditShown)
    240 		hideAdvEdit();
    241 	else
    242 		showAdvEdit();
    243 }
    244 
    245 void drawTranspose(void)
    246 {
    247 	drawFramework(0,    92,  53,  16, FRAMEWORK_TYPE1);
    248 	drawFramework(53,   92, 119,  16, FRAMEWORK_TYPE1);
    249 	drawFramework(172,  92, 119,  16, FRAMEWORK_TYPE1);
    250 	drawFramework(0,   108,  53,  65, FRAMEWORK_TYPE1);
    251 	drawFramework(53,  108, 119,  65, FRAMEWORK_TYPE1);
    252 	drawFramework(172, 108, 119,  65, FRAMEWORK_TYPE1);
    253 
    254 	textOutShadow(4,    95, PAL_FORGRND, PAL_DSKTOP2, "Transp.");
    255 	textOutShadow(58,   95, PAL_FORGRND, PAL_DSKTOP2, "Current instrument");
    256 	textOutShadow(188,  95, PAL_FORGRND, PAL_DSKTOP2, "All instruments");
    257 	textOutShadow(4,   114, PAL_FORGRND, PAL_DSKTOP2, "Track");
    258 	textOutShadow(4,   129, PAL_FORGRND, PAL_DSKTOP2, "Pattern");
    259 	textOutShadow(4,   144, PAL_FORGRND, PAL_DSKTOP2, "Song");
    260 	textOutShadow(4,   159, PAL_FORGRND, PAL_DSKTOP2, "Block");
    261 
    262 	showPushButton(PB_TRANSP_CUR_INS_TRK_UP);
    263 	showPushButton(PB_TRANSP_CUR_INS_TRK_DN);
    264 	showPushButton(PB_TRANSP_CUR_INS_TRK_12UP);
    265 	showPushButton(PB_TRANSP_CUR_INS_TRK_12DN);
    266 	showPushButton(PB_TRANSP_ALL_INS_TRK_UP);
    267 	showPushButton(PB_TRANSP_ALL_INS_TRK_DN);
    268 	showPushButton(PB_TRANSP_ALL_INS_TRK_12UP);
    269 	showPushButton(PB_TRANSP_ALL_INS_TRK_12DN);
    270 	showPushButton(PB_TRANSP_CUR_INS_PAT_UP);
    271 	showPushButton(PB_TRANSP_CUR_INS_PAT_DN);
    272 	showPushButton(PB_TRANSP_CUR_INS_PAT_12UP);
    273 	showPushButton(PB_TRANSP_CUR_INS_PAT_12DN);
    274 	showPushButton(PB_TRANSP_ALL_INS_PAT_UP);
    275 	showPushButton(PB_TRANSP_ALL_INS_PAT_DN);
    276 	showPushButton(PB_TRANSP_ALL_INS_PAT_12UP);
    277 	showPushButton(PB_TRANSP_ALL_INS_PAT_12DN);
    278 	showPushButton(PB_TRANSP_CUR_INS_SNG_UP);
    279 	showPushButton(PB_TRANSP_CUR_INS_SNG_DN);
    280 	showPushButton(PB_TRANSP_CUR_INS_SNG_12UP);
    281 	showPushButton(PB_TRANSP_CUR_INS_SNG_12DN);
    282 	showPushButton(PB_TRANSP_ALL_INS_SNG_UP);
    283 	showPushButton(PB_TRANSP_ALL_INS_SNG_DN);
    284 	showPushButton(PB_TRANSP_ALL_INS_SNG_12UP);
    285 	showPushButton(PB_TRANSP_ALL_INS_SNG_12DN);
    286 	showPushButton(PB_TRANSP_CUR_INS_BLK_UP);
    287 	showPushButton(PB_TRANSP_CUR_INS_BLK_DN);
    288 	showPushButton(PB_TRANSP_CUR_INS_BLK_12UP);
    289 	showPushButton(PB_TRANSP_CUR_INS_BLK_12DN);
    290 	showPushButton(PB_TRANSP_ALL_INS_BLK_UP);
    291 	showPushButton(PB_TRANSP_ALL_INS_BLK_DN);
    292 	showPushButton(PB_TRANSP_ALL_INS_BLK_12UP);
    293 	showPushButton(PB_TRANSP_ALL_INS_BLK_12DN);
    294 }
    295 
    296 void showTranspose(void)
    297 {
    298 	if (ui.extendedPatternEditor)
    299 		exitPatternEditorExtended();
    300 
    301 	hideTopScreen();
    302 	showTopScreen(false);
    303 
    304 	ui.transposeShown = true;
    305 	ui.scopesShown = false;
    306 	drawTranspose();
    307 }
    308 
    309 void hideTranspose(void)
    310 {
    311 	hidePushButton(PB_TRANSP_CUR_INS_TRK_UP);
    312 	hidePushButton(PB_TRANSP_CUR_INS_TRK_DN);
    313 	hidePushButton(PB_TRANSP_CUR_INS_TRK_12UP);
    314 	hidePushButton(PB_TRANSP_CUR_INS_TRK_12DN);
    315 	hidePushButton(PB_TRANSP_ALL_INS_TRK_UP);
    316 	hidePushButton(PB_TRANSP_ALL_INS_TRK_DN);
    317 	hidePushButton(PB_TRANSP_ALL_INS_TRK_12UP);
    318 	hidePushButton(PB_TRANSP_ALL_INS_TRK_12DN);
    319 	hidePushButton(PB_TRANSP_CUR_INS_PAT_UP);
    320 	hidePushButton(PB_TRANSP_CUR_INS_PAT_DN);
    321 	hidePushButton(PB_TRANSP_CUR_INS_PAT_12UP);
    322 	hidePushButton(PB_TRANSP_CUR_INS_PAT_12DN);
    323 	hidePushButton(PB_TRANSP_ALL_INS_PAT_UP);
    324 	hidePushButton(PB_TRANSP_ALL_INS_PAT_DN);
    325 	hidePushButton(PB_TRANSP_ALL_INS_PAT_12UP);
    326 	hidePushButton(PB_TRANSP_ALL_INS_PAT_12DN);
    327 	hidePushButton(PB_TRANSP_CUR_INS_SNG_UP);
    328 	hidePushButton(PB_TRANSP_CUR_INS_SNG_DN);
    329 	hidePushButton(PB_TRANSP_CUR_INS_SNG_12UP);
    330 	hidePushButton(PB_TRANSP_CUR_INS_SNG_12DN);
    331 	hidePushButton(PB_TRANSP_ALL_INS_SNG_UP);
    332 	hidePushButton(PB_TRANSP_ALL_INS_SNG_DN);
    333 	hidePushButton(PB_TRANSP_ALL_INS_SNG_12UP);
    334 	hidePushButton(PB_TRANSP_ALL_INS_SNG_12DN);
    335 	hidePushButton(PB_TRANSP_CUR_INS_BLK_UP);
    336 	hidePushButton(PB_TRANSP_CUR_INS_BLK_DN);
    337 	hidePushButton(PB_TRANSP_CUR_INS_BLK_12UP);
    338 	hidePushButton(PB_TRANSP_CUR_INS_BLK_12DN);
    339 	hidePushButton(PB_TRANSP_ALL_INS_BLK_UP);
    340 	hidePushButton(PB_TRANSP_ALL_INS_BLK_DN);
    341 	hidePushButton(PB_TRANSP_ALL_INS_BLK_12UP);
    342 	hidePushButton(PB_TRANSP_ALL_INS_BLK_12DN);
    343 
    344 	ui.transposeShown = false;
    345 	ui.scopesShown = true;
    346 	drawScopeFramework();
    347 }
    348 
    349 void toggleTranspose(void)
    350 {
    351 	if (ui.transposeShown)
    352 		hideTranspose();
    353 	else
    354 		showTranspose();
    355 }
    356 
    357 // ----- PATTERN CURSOR FUNCTIONS -----
    358 
    359 void cursorChannelLeft(void)
    360 {
    361 	cursor.object = CURSOR_EFX2;
    362 
    363 	if (cursor.ch == 0)
    364 	{
    365 		cursor.ch = (uint8_t)(song.numChannels - 1);
    366 		if (ui.pattChanScrollShown)
    367 			setScrollBarPos(SB_CHAN_SCROLL, song.numChannels, true);
    368 	}
    369 	else
    370 	{
    371 		cursor.ch--;
    372 		if (ui.pattChanScrollShown)
    373 		{
    374 			if (cursor.ch < ui.channelOffset)
    375 				scrollBarScrollUp(SB_CHAN_SCROLL, 1);
    376 		}
    377 	}
    378 }
    379 
    380 void cursorChannelRight(void)
    381 {
    382 	cursor.object = CURSOR_NOTE;
    383 
    384 	if (cursor.ch >= song.numChannels-1)
    385 	{
    386 		cursor.ch = 0;
    387 		if (ui.pattChanScrollShown)
    388 			setScrollBarPos(SB_CHAN_SCROLL, 0, true);
    389 	}
    390 	else
    391 	{
    392 		cursor.ch++;
    393 		if (ui.pattChanScrollShown && cursor.ch >= ui.channelOffset+ui.numChannelsShown)
    394 			scrollBarScrollDown(SB_CHAN_SCROLL, 1);
    395 	}
    396 }
    397 
    398 void cursorTabLeft(void)
    399 {
    400 	if (cursor.object == CURSOR_NOTE)
    401 		cursorChannelLeft();
    402 
    403 	cursor.object = CURSOR_NOTE;
    404 	ui.updatePatternEditor = true;
    405 }
    406 
    407 void cursorTabRight(void)
    408 {
    409 	cursorChannelRight();
    410 	cursor.object = CURSOR_NOTE;
    411 	ui.updatePatternEditor = true;
    412 }
    413 
    414 void chanLeft(void)
    415 {
    416 	cursorChannelLeft();
    417 	cursor.object = CURSOR_NOTE;
    418 	ui.updatePatternEditor = true;
    419 }
    420 
    421 void chanRight(void)
    422 {
    423 	cursorChannelRight();
    424 	cursor.object = CURSOR_NOTE;
    425 	ui.updatePatternEditor = true;
    426 }
    427 
    428 void cursorLeft(void)
    429 {
    430 	cursor.object--;
    431 
    432 	if (!config.ptnShowVolColumn)
    433 	{
    434 		while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
    435 			cursor.object--;
    436 	}
    437 
    438 	if (cursor.object == -1)
    439 	{
    440 		cursor.object = CURSOR_EFX2;
    441 		cursorChannelLeft();
    442 	}
    443 
    444 	ui.updatePatternEditor = true;
    445 }
    446 
    447 void cursorRight(void)
    448 {
    449 	cursor.object++;
    450 
    451 	if (!config.ptnShowVolColumn)
    452 	{
    453 		while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
    454 			cursor.object++;
    455 	}
    456 
    457 	if (cursor.object == 8)
    458 	{
    459 		cursor.object = CURSOR_NOTE;
    460 		cursorChannelRight();
    461 	}
    462 
    463 	ui.updatePatternEditor = true;
    464 }
    465 
    466 void showPatternEditor(void)
    467 {
    468 	ui.patternEditorShown = true;
    469 	updateChanNums();
    470 	drawPatternBorders();
    471 	ui.updatePatternEditor = true;
    472 }
    473 
    474 void hidePatternEditor(void)
    475 {
    476 	hideScrollBar(SB_CHAN_SCROLL);
    477 	hidePushButton(PB_CHAN_SCROLL_LEFT);
    478 	hidePushButton(PB_CHAN_SCROLL_RIGHT);
    479 
    480 	ui.patternEditorShown = false;
    481 }
    482 
    483 static void updatePatternEditorGUI(void)
    484 {
    485 	uint16_t i;
    486 	pushButton_t *p;
    487 	textBox_t *t;
    488 
    489 	if (ui.extendedPatternEditor)
    490 	{
    491 		// extended pattern editor
    492 
    493 		// instrument names
    494 		t = &textBoxes[TB_INST1];
    495 		for (i = 0; i < 8; i++, t++)
    496 		{
    497 			if (i < 4)
    498 			{
    499 				t->x = 406;
    500 				t->y = 5 + (i * 11);
    501 			}
    502 			else
    503 			{
    504 				t->x = 529;
    505 				t->y = 5 + ((i - 4) * 11);
    506 			}
    507 
    508 			t->w = 99;
    509 			t->renderW = t->w - (t->tx * 2);
    510 		}
    511 
    512 		scrollBars[SB_POS_ED].h = 23;
    513 
    514 		pushButtons[PB_POSED_POS_DOWN].y = 38;
    515 		pushButtons[PB_POSED_PATT_UP].y = 20;
    516 		pushButtons[PB_POSED_PATT_DOWN].y = 20;
    517 		pushButtons[PB_POSED_DEL].y = 35;
    518 		pushButtons[PB_SWAP_BANK].caption = "Swap B.";
    519 		pushButtons[PB_SWAP_BANK].caption2 = NULL;
    520 		pushButtons[PB_SWAP_BANK].x = 162;
    521 		pushButtons[PB_SWAP_BANK].y = 35;
    522 		pushButtons[PB_SWAP_BANK].w = 53;
    523 		pushButtons[PB_SWAP_BANK].h = 16;
    524 		pushButtons[PB_POSED_LEN_UP].x = 180;
    525 		pushButtons[PB_POSED_LEN_UP].y = 3;
    526 		pushButtons[PB_POSED_LEN_DOWN].x = 197;
    527 		pushButtons[PB_POSED_LEN_DOWN].y = 3;
    528 		pushButtons[PB_POSED_REP_UP].x = 180;
    529 		pushButtons[PB_POSED_REP_UP].y = 17;
    530 		pushButtons[PB_POSED_REP_DOWN].x = 197;
    531 		pushButtons[PB_POSED_REP_DOWN].y = 17;
    532 		pushButtons[PB_PATT_UP].x = 267;
    533 		pushButtons[PB_PATT_UP].y = 37;
    534 		pushButtons[PB_PATT_DOWN].x = 284;
    535 		pushButtons[PB_PATT_DOWN].y = 37;
    536 		pushButtons[PB_PATTLEN_UP].x = 348;
    537 		pushButtons[PB_PATTLEN_UP].y = 37;
    538 		pushButtons[PB_PATTLEN_DOWN].x = 365;
    539 		pushButtons[PB_PATTLEN_DOWN].y = 37;
    540 
    541 		// instrument switcher
    542 		p = &pushButtons[PB_RANGE1];
    543 		for (i = 0; i < 16; i++, p++)
    544 		{
    545 			p->w = iSwitchExtW[i & 3];
    546 			p->x = iSwitchExtX[i & 3];
    547 			p->y = iSwitchExtY[i & 7];
    548 		}
    549 	}
    550 	else
    551 	{
    552 		// instrument names
    553 		t = &textBoxes[TB_INST1];
    554 		for (i = 0; i < 8; i++, t++)
    555 		{
    556 			t->y = 5 + (i * 11);
    557 			t->x = 446;
    558 			t->w = 140;
    559 			t->renderW = t->w - (t->tx * 2);
    560 		}
    561 
    562 		// normal pattern editor
    563 
    564 		scrollBars[SB_POS_ED].h = 21;
    565 
    566 		pushButtons[PB_POSED_POS_DOWN].y = 36;
    567 		pushButtons[PB_POSED_PATT_UP].y = 19;
    568 		pushButtons[PB_POSED_PATT_DOWN].y = 19;
    569 		pushButtons[PB_POSED_DEL].y = 33;
    570 		pushButtons[PB_SWAP_BANK].caption = "Swap";
    571 		pushButtons[PB_SWAP_BANK].caption2 = "Bank";
    572 		pushButtons[PB_SWAP_BANK].x = 590;
    573 		pushButtons[PB_SWAP_BANK].y = 144;
    574 		pushButtons[PB_SWAP_BANK].w = 39;
    575 		pushButtons[PB_SWAP_BANK].h = 27;
    576 		pushButtons[PB_POSED_LEN_UP].x = 74;
    577 		pushButtons[PB_POSED_LEN_UP].y = 50;
    578 		pushButtons[PB_POSED_LEN_DOWN].x = 91;
    579 		pushButtons[PB_POSED_LEN_DOWN].y = 50;
    580 		pushButtons[PB_POSED_REP_UP].x = 74;
    581 		pushButtons[PB_POSED_REP_UP].y = 62;
    582 		pushButtons[PB_POSED_REP_DOWN].x = 91;
    583 		pushButtons[PB_POSED_REP_DOWN].y = 62;
    584 		pushButtons[PB_PATT_UP].x = 253;
    585 		pushButtons[PB_PATT_UP].y = 34;
    586 		pushButtons[PB_PATT_DOWN].x = 270;
    587 		pushButtons[PB_PATT_DOWN].y = 34;
    588 		pushButtons[PB_PATTLEN_UP].x = 253;
    589 		pushButtons[PB_PATTLEN_UP].y = 48;
    590 		pushButtons[PB_PATTLEN_DOWN].x = 270;
    591 		pushButtons[PB_PATTLEN_DOWN].y = 48;
    592 
    593 		// instrument switcher
    594 		p = &pushButtons[PB_RANGE1];
    595 		for (i = 0; i < 16; i++, p++)
    596 		{
    597 			p->w = 39;
    598 			p->x = 590;
    599 			p->y = iSwitchY[i & 7];
    600 		}
    601 	}
    602 }
    603 
    604 void patternEditorExtended(void)
    605 {
    606 	// backup old screen flags
    607 	ui._aboutScreenShown = ui.aboutScreenShown;
    608 	ui._helpScreenShown = ui.helpScreenShown;
    609 	ui._configScreenShown = ui.configScreenShown;
    610 	ui._diskOpShown = ui.diskOpShown;
    611 	ui._nibblesShown = ui.nibblesShown;
    612 	ui._transposeShown = ui.transposeShown;
    613 	ui._instEditorShown = ui.instEditorShown;
    614 	ui._instEditorExtShown = ui.instEditorExtShown;
    615 	ui._sampleEditorExtShown = ui.sampleEditorExtShown;
    616 	ui._sampleEditorEffectsShown = ui.sampleEditorEffectsShown;
    617 	ui._patternEditorShown = ui.patternEditorShown;
    618 	ui._sampleEditorShown = ui.sampleEditorShown;
    619 	ui._advEditShown= ui.advEditShown;
    620 	ui._wavRendererShown = ui.wavRendererShown;
    621 	ui._trimScreenShown = ui.trimScreenShown;
    622 
    623 	hideTopScreen();
    624 	hideSampleEditor();
    625 	hideInstEditor();
    626 
    627 	ui.extendedPatternEditor = true;
    628 	ui.patternEditorShown = true;
    629 	updatePatternEditorGUI(); // change pattern editor layout (based on ui.extended flag)
    630 	ui.updatePatternEditor = true; // redraw pattern editor
    631 
    632 	drawFramework(0,    0, 112, 53, FRAMEWORK_TYPE1);
    633 	drawFramework(112,  0, 106, 33, FRAMEWORK_TYPE1);
    634 	drawFramework(112, 33, 106, 20, FRAMEWORK_TYPE1);
    635 	drawFramework(218,  0, 168, 53, FRAMEWORK_TYPE1);
    636 
    637 	// pos ed. stuff
    638 
    639 	drawFramework(2,  2, 51, 20, FRAMEWORK_TYPE2);
    640 	drawFramework(2, 31, 51, 20, FRAMEWORK_TYPE2);
    641 
    642 	drawFramework(0, 53, SCREEN_W, 15, FRAMEWORK_TYPE1);
    643 
    644 	showScrollBar(SB_POS_ED);
    645 
    646 	showPushButton(PB_POSED_POS_UP);
    647 	showPushButton(PB_POSED_POS_DOWN);
    648 	showPushButton(PB_POSED_INS);
    649 	showPushButton(PB_POSED_PATT_UP);
    650 	showPushButton(PB_POSED_PATT_DOWN);
    651 	showPushButton(PB_POSED_DEL);
    652 	showPushButton(PB_POSED_LEN_UP);
    653 	showPushButton(PB_POSED_LEN_DOWN);
    654 	showPushButton(PB_POSED_REP_UP);
    655 	showPushButton(PB_POSED_REP_DOWN);
    656 	showPushButton(PB_SWAP_BANK);
    657 	showPushButton(PB_PATT_UP);
    658 	showPushButton(PB_PATT_DOWN);
    659 	showPushButton(PB_PATTLEN_UP);
    660 	showPushButton(PB_PATTLEN_DOWN);
    661 
    662 	showPushButton(PB_EXIT_EXT_PATT);
    663 
    664 	textOutShadow(116,  5, PAL_FORGRND, PAL_DSKTOP2, "Sng.len.");
    665 	textOutShadow(116, 19, PAL_FORGRND, PAL_DSKTOP2, "Repst.");
    666 	textOutShadow(222, 39, PAL_FORGRND, PAL_DSKTOP2, "Ptn.");
    667 	textOutShadow(305, 39, PAL_FORGRND, PAL_DSKTOP2, "Ln.");
    668 
    669 	textOutShadow(4, 56, PAL_FORGRND, PAL_DSKTOP2, "Global volume");
    670 	textOutShadow(545, 56, PAL_FORGRND, PAL_DSKTOP2, "Time");
    671 	charOutShadow(591, 56, PAL_FORGRND, PAL_DSKTOP2, ':');
    672 	charOutShadow(611, 56, PAL_FORGRND, PAL_DSKTOP2, ':');
    673 
    674 	ui.instrSwitcherShown = true;
    675 	showInstrumentSwitcher();
    676 
    677 	drawSongLength();
    678 	drawSongLoopStart();
    679 	drawEditPattern(editor.editPattern);
    680 	drawPatternLength(editor.editPattern);
    681 	drawPosEdNums(editor.songPos);
    682 	ui.updatePosSections = true;
    683 
    684 	// kludge to fix scrollbar thumb when the scrollbar height changes during playback
    685 	if (songPlaying)
    686 		setScrollBarPos(SB_POS_ED, editor.songPos, false);
    687 }
    688 
    689 void exitPatternEditorExtended(void)
    690 {
    691 	ui.extendedPatternEditor = false;
    692 	updatePatternEditorGUI();
    693 	hidePushButton(PB_EXIT_EXT_PATT);
    694 
    695 	// set back top screen button maps
    696 
    697 	// set back old screen flags
    698 	ui.aboutScreenShown = ui._aboutScreenShown;
    699 	ui.helpScreenShown = ui._helpScreenShown;
    700 	ui.configScreenShown = ui._configScreenShown;
    701 	ui.diskOpShown = ui._diskOpShown;
    702 	ui.nibblesShown = ui._nibblesShown;
    703 	ui.transposeShown = ui._transposeShown;
    704 	ui.instEditorShown = ui._instEditorShown;
    705 	ui.instEditorExtShown = ui._instEditorExtShown;
    706 	ui.sampleEditorExtShown = ui._sampleEditorExtShown;
    707 	ui.sampleEditorEffectsShown = ui._sampleEditorEffectsShown;
    708 	ui.patternEditorShown = ui._patternEditorShown;
    709 	ui.sampleEditorShown = ui._sampleEditorShown;
    710 	ui.advEditShown = ui._advEditShown;
    711 	ui.wavRendererShown = ui._wavRendererShown;
    712 	ui.trimScreenShown = ui.trimScreenShown;
    713 
    714 	showTopScreen(true);
    715 	showBottomScreen();
    716 
    717 	// kludge to fix scrollbar thumb when the scrollbar height changes during playback
    718 	if (songPlaying)
    719 		setScrollBarPos(SB_POS_ED, editor.songPos, false);
    720 }
    721 
    722 void togglePatternEditorExtended(void)
    723 {
    724 	if (ui.extendedPatternEditor)
    725 		exitPatternEditorExtended();
    726 	else
    727 		patternEditorExtended();
    728 }
    729 
    730 void clearPattMark(void)
    731 {
    732 	memset((void *)&pattMark, 0, sizeof (pattMark));
    733 
    734 	lastMarkX1 = -1;
    735 	lastMarkX2 = -1;
    736 	lastMarkY1 = -1;
    737 	lastMarkY2 = -1;
    738 }
    739 
    740 void checkMarkLimits(void)
    741 {
    742 	volatile int16_t markX1 = pattMark.markX1;
    743 	volatile int16_t markX2 = pattMark.markX2;
    744 	volatile int16_t markY1 = pattMark.markY1;
    745 	volatile int16_t markY2 = pattMark.markY2;
    746 
    747 	const int16_t limitY = patternNumRows[editor.editPattern];
    748 	markY1 = CLAMP(markY1, 0, limitY);
    749 	markY2 = CLAMP(markY2, 0, limitY);
    750 
    751 	const int16_t limitX = (int16_t)(song.numChannels - 1);
    752 	markX1 = CLAMP(markX1, 0, limitX);
    753 	markX2 = CLAMP(markX2, 0, limitX);
    754 
    755 	// XXX: will probably never happen? FT2 has this in CheckMarkLimits() though...
    756 	if (markX1 > markX2)
    757 		markX1 = markX2;
    758 
    759 	pattMark.markX1 = markX1;
    760 	pattMark.markX2 = markX2;
    761 	pattMark.markY1 = markY1;
    762 	pattMark.markY2 = markY2;
    763 }
    764 
    765 static int8_t mouseXToCh(void) // used to get channel num from mouse x (for pattern marking)
    766 {
    767 	assert(ui.patternChannelWidth > 0);
    768 	if (ui.patternChannelWidth == 0)
    769 		return 0;
    770 
    771 	int32_t mouseX = mouse.x - 29;
    772 	mouseX = CLAMP(mouseX, 0, 573);
    773 
    774 	const int8_t chEnd = (ui.channelOffset + ui.numChannelsShown) - 1;
    775 
    776 	int8_t ch = ui.channelOffset + (int8_t)(mouseX / ui.patternChannelWidth);
    777 	ch = CLAMP(ch, 0, chEnd);
    778 
    779 	// in some setups there can be non-used channels to the right, do clamping
    780 	if (ch >= song.numChannels)
    781 		ch = (int8_t)(song.numChannels - 1);
    782 
    783 	return ch;
    784 }
    785 
    786 static int16_t mouseYToRow(void) // used to get row num from mouse y (for pattern marking)
    787 {
    788 	const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnStretch][ui.pattChanScrollShown][ui.extendedPatternEditor];
    789 
    790 	// clamp mouse y to boundaries
    791 	const int16_t maxY = ui.pattChanScrollShown ? 382 : 396;
    792 	const int16_t my = (int16_t)CLAMP(mouse.y, pattCoordsMouse->upperRowsY, maxY);
    793 
    794 	const uint8_t charHeight = config.ptnStretch ? 11 : 8;
    795 
    796 	// test top/middle/bottom rows
    797 	if (my < pattCoordsMouse->midRowY)
    798 	{
    799 		// top rows
    800 		int16_t row = editor.row - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight));
    801 		if (row < 0)
    802 			row = 0;
    803 
    804 		return row;
    805 	}
    806 	else if (my >= pattCoordsMouse->midRowY && my <= pattCoordsMouse->midRowY+10)
    807 	{
    808 		// current row (middle)
    809 		return editor.row;
    810 	}
    811 	else
    812 	{
    813 		// bottom rows
    814 		int16_t row = (editor.row + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight);
    815 
    816 		// prevent being able to mark the next unseen row on the bottom (in some configurations)
    817 		const uint8_t mode = (ui.extendedPatternEditor * 4) + (config.ptnStretch * 2) + ui.pattChanScrollShown;
    818 
    819 		const int16_t maxRow = (ptnNumRows[mode] + (editor.row - ptnLineSub[mode])) - 1;
    820 		if (row > maxRow)
    821 			row = maxRow;
    822 
    823 		// clamp to pattern length
    824 		const int16_t patternLen = patternNumRows[editor.editPattern];
    825 		if (row >= patternLen)
    826 			row = patternLen - 1;
    827 
    828 		return row;
    829 	}
    830 }
    831 
    832 void handlePatternDataMouseDown(bool mouseButtonHeld)
    833 {
    834 	int16_t y1, y2;
    835 
    836 	// non-FT2 feature: Use right mouse button to remove pattern marking
    837 	if (mouse.rightButtonPressed)
    838 	{
    839 		clearPattMark();
    840 		ui.updatePatternEditor = true;
    841 		return;
    842 	}
    843 
    844 	if (!mouseButtonHeld)
    845 	{
    846 		// we clicked inside the pattern data area for the first time, set initial vars
    847 
    848 		mouse.lastUsedObjectType = OBJECT_PATTERNMARK;
    849 
    850 		lastMouseX = mouse.x;
    851 		lastMouseY = mouse.y;
    852 
    853 		lastChMark = mouseXToCh();
    854 		lastRowMark = mouseYToRow();
    855 
    856 		pattMark.markX1 = lastChMark;
    857 		pattMark.markX2 = lastChMark;
    858 		pattMark.markY1 = lastRowMark;
    859 		pattMark.markY2 = lastRowMark + 1;
    860 
    861 		checkMarkLimits();
    862 
    863 		ui.updatePatternEditor = true;
    864 		return;
    865 	}
    866 
    867 	// we're holding down the mouse button inside the pattern data area
    868 
    869 	bool forceMarking = songPlaying;
    870 
    871 	// scroll left/right with mouse
    872 	if (ui.pattChanScrollShown)
    873 	{
    874 		if (mouse.x < 29)
    875 		{
    876 			scrollBarScrollUp(SB_CHAN_SCROLL, 1);
    877 			forceMarking = true;
    878 		}
    879 		else if (mouse.x > 604)
    880 		{
    881 			scrollBarScrollDown(SB_CHAN_SCROLL, 1);
    882 			forceMarking = true;
    883 		}
    884 	}
    885 
    886 	// mark channels
    887 	if (forceMarking || lastMouseX != mouse.x)
    888 	{
    889 		lastMouseX = mouse.x;
    890 
    891 		int8_t chTmp = mouseXToCh();
    892 		if (chTmp < lastChMark)
    893 		{
    894 			pattMark.markX1 = chTmp;
    895 			pattMark.markX2 = lastChMark;
    896 		}
    897 		else
    898 		{
    899 			pattMark.markX2 = chTmp;
    900 			pattMark.markX1 = lastChMark;
    901 		}
    902 
    903 		if (lastMarkX1 != pattMark.markX1 || lastMarkX2 != pattMark.markX2)
    904 		{
    905 			checkMarkLimits();
    906 			ui.updatePatternEditor = true;
    907 
    908 			lastMarkX1 = pattMark.markX1;
    909 			lastMarkX2 = pattMark.markX2;
    910 		}
    911 	}
    912 
    913 	// scroll down/up with mouse (if song is not playing)
    914 	if (!songPlaying)
    915 	{
    916 		y1 = ui.extendedPatternEditor ? 71 : 176;
    917 		y2 = ui.pattChanScrollShown ? 382 : 396;
    918 
    919 		if (mouse.y < y1)
    920 		{
    921 			if (editor.row > 0)
    922 				setPos(-1, editor.row - 1, true);
    923 
    924 			forceMarking = true;
    925 			ui.updatePatternEditor = true;
    926 		}
    927 		else if (mouse.y > y2)
    928 		{
    929 			const int16_t numRows = patternNumRows[editor.editPattern];
    930 			if (editor.row < numRows-1)
    931 				setPos(-1, editor.row + 1, true);
    932 
    933 			forceMarking = true;
    934 			ui.updatePatternEditor = true;
    935 		}
    936 	}
    937 
    938 	// mark rows
    939 	if (forceMarking || lastMouseY != mouse.y)
    940 	{
    941 		lastMouseY = mouse.y;
    942 
    943 		const int16_t rowTmp = mouseYToRow();
    944 		if (rowTmp < lastRowMark)
    945 		{
    946 			pattMark.markY1 = rowTmp;
    947 			pattMark.markY2 = lastRowMark + 1;
    948 		}
    949 		else
    950 		{
    951 			pattMark.markY2 = rowTmp + 1;
    952 			pattMark.markY1 = lastRowMark;
    953 		}
    954 
    955 		if (lastMarkY1 != pattMark.markY1 || lastMarkY2 != pattMark.markY2)
    956 		{
    957 			checkMarkLimits();
    958 			ui.updatePatternEditor = true;
    959 
    960 			lastMarkY1 = pattMark.markY1;
    961 			lastMarkY2 = pattMark.markY2;
    962 		}
    963 	}
    964 }
    965 
    966 void rowOneUpWrap(void)
    967 {
    968 	const bool audioWasntLocked = !audio.locked;
    969 	if (audioWasntLocked)
    970 		lockAudio();
    971 
    972 	if (song.currNumRows > 0)
    973 	{
    974 		song.row = (song.row - 1 + song.currNumRows) % song.currNumRows;
    975 
    976 		if (!songPlaying)
    977 		{
    978 			editor.row = (uint8_t)song.row;
    979 			ui.updatePatternEditor = true;
    980 		}
    981 	}
    982 
    983 	if (audioWasntLocked)
    984 		unlockAudio();
    985 }
    986 
    987 void rowOneDownWrap(void)
    988 {
    989 	const bool audioWasntLocked = !audio.locked;
    990 	if (audioWasntLocked)
    991 		lockAudio();
    992 
    993 	if (songPlaying)
    994 	{
    995 		song.tick = 2;
    996 	}
    997 	else if (song.currNumRows > 0)
    998 	{
    999 		song.row = (song.row + 1 + song.currNumRows) % song.currNumRows;
   1000 		editor.row = (uint8_t)song.row;
   1001 		ui.updatePatternEditor = true;
   1002 	}
   1003 
   1004 	if (audioWasntLocked)
   1005 		unlockAudio();
   1006 }
   1007 
   1008 void rowUp(uint16_t amount)
   1009 {
   1010 	const bool audioWasntLocked = !audio.locked;
   1011 	if (audioWasntLocked)
   1012 		lockAudio();
   1013 
   1014 	song.row -= amount;
   1015 	if (song.row < 0)
   1016 		song.row = 0;
   1017 
   1018 	if (!songPlaying)
   1019 	{
   1020 		editor.row = (uint8_t)song.row;
   1021 		ui.updatePatternEditor = true;
   1022 	}
   1023 
   1024 	if (audioWasntLocked)
   1025 		unlockAudio();
   1026 }
   1027 
   1028 void rowDown(uint16_t amount)
   1029 {
   1030 	const bool audioWasntLocked = !audio.locked;
   1031 	if (audioWasntLocked)
   1032 		lockAudio();
   1033 
   1034 	song.row += amount;
   1035 	if (song.row >= song.currNumRows)
   1036 		song.row = song.currNumRows - 1;
   1037 
   1038 	if (!songPlaying)
   1039 	{
   1040 		editor.row = (uint8_t)song.row;
   1041 		ui.updatePatternEditor = true;
   1042 	}
   1043 
   1044 	if (audioWasntLocked)
   1045 		unlockAudio();
   1046 }
   1047 
   1048 void keybPattMarkUp(void)
   1049 {
   1050 	int8_t xPos = cursor.ch;
   1051 	int16_t row = editor.row;
   1052 
   1053 	if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
   1054 	{
   1055 		pattMark.markX1 = xPos;
   1056 		pattMark.markX2 = xPos;
   1057 		pattMark.markY1 = row;
   1058 		pattMark.markY2 = row + 1;
   1059 	}
   1060 
   1061 	if (row == pattMark.markY1-1)
   1062 	{
   1063 		pattMark.markY1 = row;
   1064 	}
   1065 	else if (row == pattMark.markY2)
   1066 	{
   1067 		pattMark.markY2 = row - 1;
   1068 	}
   1069 	else if (row != pattMark.markY1 && row != pattMark.markY2)
   1070 	{
   1071 		pattMark.markX1 = xPos;
   1072 		pattMark.markX2 = xPos;
   1073 		pattMark.markY1 = row;
   1074 		pattMark.markY2 = row + 1;
   1075 
   1076 	}
   1077 
   1078 	checkMarkLimits();
   1079 	rowOneUpWrap();
   1080 }
   1081 
   1082 void keybPattMarkDown(void)
   1083 {
   1084 	int8_t xPos = cursor.ch;
   1085 	int16_t row = editor.row;
   1086 
   1087 	if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
   1088 	{
   1089 		pattMark.markX1 = xPos;
   1090 		pattMark.markX2 = xPos;
   1091 		pattMark.markY1 = row;
   1092 		pattMark.markY2 = row + 1;
   1093 	}
   1094 
   1095 	if (row == pattMark.markY2)
   1096 	{
   1097 		pattMark.markY2 = row + 1;
   1098 	}
   1099 	else if (row == pattMark.markY1-1)
   1100 	{
   1101 		pattMark.markY1 = row + 2;
   1102 	}
   1103 	else if (row != pattMark.markY1 && row != pattMark.markY2)
   1104 	{
   1105 		pattMark.markX1 = xPos;
   1106 		pattMark.markX2 = xPos;
   1107 		pattMark.markY1 = row;
   1108 		pattMark.markY2 = row + 1;
   1109 	}
   1110 
   1111 	checkMarkLimits();
   1112 	rowOneDownWrap();
   1113 }
   1114 
   1115 void keybPattMarkLeft(void)
   1116 {
   1117 	int8_t xPos = cursor.ch;
   1118 	int16_t row = editor.row;
   1119 
   1120 	if (row != pattMark.markY1-1 && row != pattMark.markY2)
   1121 	{
   1122 		pattMark.markY1 = row - 1;
   1123 		pattMark.markY2 = row;
   1124 	}
   1125 
   1126 	if (xPos == pattMark.markX1)
   1127 	{
   1128 		pattMark.markX1 = xPos - 1;
   1129 	}
   1130 	else if (xPos == pattMark.markX2)
   1131 	{
   1132 		pattMark.markX2 = xPos - 1;
   1133 	}
   1134 	else if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
   1135 	{
   1136 		pattMark.markX1 = xPos - 1;
   1137 		pattMark.markX2 = xPos;
   1138 		pattMark.markY1 = row - 1;
   1139 		pattMark.markY2 = row;
   1140 	}
   1141 
   1142 	checkMarkLimits();
   1143 	chanLeft();
   1144 }
   1145 
   1146 void keybPattMarkRight(void)
   1147 {
   1148 	int8_t xPos = cursor.ch;
   1149 	int16_t row = editor.row;
   1150 
   1151 	if (row != pattMark.markY1-1 && row != pattMark.markY2)
   1152 	{
   1153 		pattMark.markY1 = row - 1;
   1154 		pattMark.markY2 = row;
   1155 	}
   1156 
   1157 	if (xPos == pattMark.markX2)
   1158 	{
   1159 		pattMark.markX2 = xPos + 1;
   1160 	}
   1161 	else if (xPos == pattMark.markX1)
   1162 	{
   1163 		pattMark.markX1 = xPos + 1;
   1164 	}
   1165 	else if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
   1166 	{
   1167 		pattMark.markX1 = xPos;
   1168 		pattMark.markX2 = xPos + 1;
   1169 		pattMark.markY1 = row - 1;
   1170 		pattMark.markY2 = row;
   1171 	}
   1172 
   1173 	checkMarkLimits();
   1174 	chanRight();
   1175 }
   1176 
   1177 bool loadTrack(UNICHAR *filenameU)
   1178 {
   1179 	note_t loadBuff[MAX_PATT_LEN];
   1180 	xtHdr_t h;
   1181 
   1182 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
   1183 	if (f == NULL)
   1184 	{
   1185 		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
   1186 		return false;
   1187 	}
   1188 
   1189 	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
   1190 	{
   1191 		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
   1192 		goto trackLoadError;
   1193 	}
   1194 
   1195 	if (h.version != 1)
   1196 	{
   1197 		okBox(0, "System message", "Incompatible format version!", NULL);
   1198 		goto trackLoadError;
   1199 	}
   1200 
   1201 	if (h.numRows > MAX_PATT_LEN)
   1202 		h.numRows = MAX_PATT_LEN;
   1203 
   1204 	int16_t numRows = patternNumRows[editor.editPattern];
   1205 	if (numRows > h.numRows)
   1206 		numRows = h.numRows;
   1207 
   1208 	if (fread(loadBuff, numRows * sizeof (note_t), 1, f) != 1)
   1209 	{
   1210 		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
   1211 		goto trackLoadError;
   1212 	}
   1213 
   1214 	if (!allocatePattern(editor.editPattern))
   1215 	{
   1216 		okBox(0, "System message", "Not enough memory!", NULL);
   1217 		goto trackLoadError;
   1218 	}
   1219 
   1220 	lockMixerCallback();
   1221 	for (int32_t i = 0; i < numRows; i++)
   1222 	{
   1223 		note_t *p = &pattern[editor.editPattern][(i * MAX_CHANNELS) + cursor.ch];
   1224 
   1225 		*p = loadBuff[i];
   1226 
   1227 		// sanitize stuff (FT2 doesn't do this!)
   1228 
   1229 		if (p->note > 97)
   1230 			p->note = 0;
   1231 
   1232 		if (p->instr > 128)
   1233 			p->instr = 0;
   1234 
   1235 		if (p->efx > 35)
   1236 		{
   1237 			p->efx = 0;
   1238 			p->efxData = 0;
   1239 		}
   1240 	}
   1241 	unlockMixerCallback();
   1242 
   1243 	fclose(f);
   1244 
   1245 	ui.updatePatternEditor = true;
   1246 	ui.updatePosSections = true;
   1247 
   1248 	diskOpSetFilename(DISKOP_ITEM_TRACK, filenameU);
   1249 	setSongModifiedFlag();
   1250 
   1251 	return true;
   1252 
   1253 trackLoadError:
   1254 	fclose(f);
   1255 	return false;
   1256 }
   1257 
   1258 bool saveTrack(UNICHAR *filenameU)
   1259 {
   1260 	note_t saveBuff[MAX_PATT_LEN];
   1261 	xtHdr_t h;
   1262 
   1263 	note_t *p = pattern[editor.editPattern];
   1264 	if (p == NULL)
   1265 	{
   1266 		okBox(0, "System message", "The current pattern is empty!", NULL);
   1267 		return false;
   1268 	}
   1269 
   1270 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
   1271 	if (f == NULL)
   1272 	{
   1273 		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
   1274 		return false;
   1275 	}
   1276 
   1277 	h.version = 1;
   1278 	h.numRows = patternNumRows[editor.editPattern];
   1279 
   1280 	for (int32_t i = 0; i < h.numRows; i++)
   1281 		saveBuff[i] = p[(i * MAX_CHANNELS) + cursor.ch];
   1282 
   1283 	if (fwrite(&h, sizeof (h), 1, f) !=  1)
   1284 	{
   1285 		fclose(f);
   1286 		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
   1287 		return false;
   1288 	}
   1289 
   1290 	if (fwrite(saveBuff, h.numRows * sizeof (note_t), 1, f) != 1)
   1291 	{
   1292 		fclose(f);
   1293 		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
   1294 		return false;
   1295 	}
   1296 
   1297 	fclose(f);
   1298 	return true;
   1299 }
   1300 
   1301 bool loadPattern(UNICHAR *filenameU)
   1302 {
   1303 	xpHdr_t h;
   1304 
   1305 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
   1306 	if (f == NULL)
   1307 	{
   1308 		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
   1309 		return false;
   1310 	}
   1311 
   1312 	if (!allocatePattern(editor.editPattern))
   1313 	{
   1314 		okBox(0, "System message", "Not enough memory!", NULL);
   1315 		goto loadPattError;
   1316 	}
   1317 
   1318 	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
   1319 	{
   1320 		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
   1321 		goto loadPattError;
   1322 	}
   1323 
   1324 	if (h.version != 1)
   1325 	{
   1326 		okBox(0, "System message", "Incompatible format version!", NULL);
   1327 		goto loadPattError;
   1328 	}
   1329 
   1330 	if (h.numRows > MAX_PATT_LEN)
   1331 		h.numRows = MAX_PATT_LEN;
   1332 
   1333 	lockMixerCallback();
   1334 
   1335 	note_t *p = pattern[editor.editPattern];
   1336 	if (fread(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
   1337 	{
   1338 		unlockMixerCallback();
   1339 		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
   1340 		goto loadPattError;
   1341 	}
   1342 
   1343 	// sanitize data (FT2 doesn't do this!)
   1344 	for (int32_t row = 0; row < h.numRows; row++)
   1345 	{
   1346 		for (int32_t ch = 0; ch < MAX_CHANNELS; ch++)
   1347 		{
   1348 			p = &pattern[editor.editPattern][(row * MAX_CHANNELS) + ch];
   1349 
   1350 			if (p->note > 97)
   1351 				p->note = 0;
   1352 
   1353 			if (p->instr > 128)
   1354 				p->instr = 128;
   1355 
   1356 			if (p->efx > 35)
   1357 			{
   1358 				p->efx = 0;
   1359 				p->efxData = 0;
   1360 			}
   1361 		}
   1362 	}
   1363 
   1364 	// set new pattern length (FT2 doesn't do this, strange...)
   1365 	song.currNumRows = patternNumRows[editor.editPattern] = h.numRows;
   1366 
   1367 	if (song.row >= song.currNumRows)
   1368 	{
   1369 		song.row = song.currNumRows-1;
   1370 		if (!songPlaying)
   1371 			editor.row = song.row;
   1372 	}
   1373 
   1374 	unlockMixerCallback();
   1375 
   1376 	fclose(f);
   1377 
   1378 	ui.updatePatternEditor = true;
   1379 	ui.updatePosSections = true;
   1380 
   1381 	diskOpSetFilename(DISKOP_ITEM_PATTERN, filenameU);
   1382 	setSongModifiedFlag();
   1383 
   1384 	return true;
   1385 
   1386 loadPattError:
   1387 	fclose(f);
   1388 	return false;
   1389 }
   1390 
   1391 bool savePattern(UNICHAR *filenameU)
   1392 {
   1393 	xpHdr_t h;
   1394 
   1395 	note_t *p = pattern[editor.editPattern];
   1396 	if (p == NULL)
   1397 	{
   1398 		okBox(0, "System message", "The current pattern is empty!", NULL);
   1399 		return false;
   1400 	}
   1401 
   1402 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
   1403 	if (f == NULL)
   1404 	{
   1405 		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
   1406 		return false;
   1407 	}
   1408 
   1409 	h.version = 1;
   1410 	h.numRows = patternNumRows[editor.editPattern];
   1411 	
   1412 	if (fwrite(&h, 1, sizeof (h), f) != sizeof (h))
   1413 	{
   1414 		fclose(f);
   1415 		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
   1416 		return false;
   1417 	}
   1418 
   1419 	if (fwrite(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
   1420 	{
   1421 		fclose(f);
   1422 		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
   1423 		return false;
   1424 	}
   1425 
   1426 	fclose(f);
   1427 	return true;
   1428 }
   1429 
   1430 void scrollChannelLeft(void)
   1431 {
   1432 	scrollBarScrollLeft(SB_CHAN_SCROLL, 1);
   1433 }
   1434 
   1435 void scrollChannelRight(void)
   1436 {
   1437 	scrollBarScrollRight(SB_CHAN_SCROLL, 1);
   1438 }
   1439 
   1440 void setChannelScrollPos(uint32_t pos)
   1441 {
   1442 	if (!ui.pattChanScrollShown)
   1443 	{
   1444 		ui.channelOffset = 0;
   1445 		return;
   1446 	}
   1447 
   1448 	if (ui.channelOffset == (uint8_t)pos)
   1449 		return;
   1450 
   1451 	ui.channelOffset = (uint8_t)pos;
   1452 
   1453 	assert(song.numChannels > ui.numChannelsShown);
   1454 	if (ui.channelOffset >= song.numChannels-ui.numChannelsShown)
   1455 		ui.channelOffset = (uint8_t)(song.numChannels-ui.numChannelsShown);
   1456 
   1457 	if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
   1458 	{
   1459 		cursor.object = CURSOR_NOTE;
   1460 		cursor.ch = (ui.channelOffset + ui.numChannelsShown) - 1;
   1461 	}
   1462 	else if (cursor.ch < ui.channelOffset)
   1463 	{
   1464 		cursor.object = CURSOR_NOTE;
   1465 		cursor.ch = ui.channelOffset;
   1466 	}
   1467 
   1468 	ui.updatePatternEditor = true;
   1469 }
   1470 
   1471 void jumpToChannel(uint8_t chNr) // for ALT+q..i ALT+a..k
   1472 {
   1473 	if (ui.sampleEditorShown || ui.instEditorShown)
   1474 		return;
   1475 
   1476 	chNr %= song.numChannels;
   1477 	if (cursor.ch == chNr)
   1478 		return;
   1479 
   1480 	if (ui.pattChanScrollShown)
   1481 	{
   1482 		assert(song.numChannels > ui.numChannelsShown);
   1483 
   1484 		if (chNr >= ui.channelOffset+ui.numChannelsShown)
   1485 			scrollBarScrollDown(SB_CHAN_SCROLL, (chNr - (ui.channelOffset + ui.numChannelsShown)) + 1);
   1486 		else if (chNr < ui.channelOffset)
   1487 			scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - chNr);
   1488 	}
   1489 
   1490 	cursor.ch = chNr; // set it here since scrollBarScrollX() changes it...
   1491 	ui.updatePatternEditor = true;
   1492 }
   1493 
   1494 void sbPosEdPos(uint32_t pos)
   1495 {
   1496 	const bool audioWasntLocked = !audio.locked;
   1497 	if (audioWasntLocked)
   1498 		lockAudio();
   1499 
   1500 	if (song.songPos != (int16_t)pos)
   1501 		setNewSongPos((int16_t)pos);
   1502 
   1503 	if (audioWasntLocked)
   1504 		unlockAudio();
   1505 }
   1506 
   1507 void pbPosEdPosUp(void)
   1508 {
   1509 	incSongPos();
   1510 }
   1511 
   1512 void pbPosEdPosDown(void)
   1513 {
   1514 	decSongPos();
   1515 }
   1516 
   1517 void pbPosEdIns(void)
   1518 {
   1519 	if (song.songLength >= 255)
   1520 		return;
   1521 
   1522 	lockMixerCallback();
   1523 
   1524 	const uint8_t oldPatt = song.orders[song.songPos];
   1525 	for (uint16_t i = 0; i < 255-song.songPos; i++)
   1526 		song.orders[255-i] = song.orders[254-i];
   1527 	song.orders[song.songPos] = oldPatt;
   1528 
   1529 	song.songLength++;
   1530 
   1531 	ui.updatePosSections = true;
   1532 	ui.updatePosEdScrollBar = true;
   1533 	setSongModifiedFlag();
   1534 
   1535 	unlockMixerCallback();
   1536 }
   1537 
   1538 void pbPosEdDel(void)
   1539 {
   1540 	if (song.songLength <= 1)
   1541 		return;
   1542 
   1543 	lockMixerCallback();
   1544 
   1545 	if (song.songPos < 254)
   1546 	{
   1547 		for (uint16_t i = 0; i < 254-song.songPos; i++)
   1548 			song.orders[song.songPos+i] = song.orders[song.songPos+1+i];
   1549 	}
   1550 
   1551 	song.songLength--;
   1552 	if (song.songLoopStart >= song.songLength)
   1553 		song.songLoopStart = song.songLength - 1;
   1554 
   1555 	if (song.songPos > song.songLength-1)
   1556 	{
   1557 		editor.songPos = song.songPos = song.songLength-1;
   1558 		setPos(song.songPos, -1, false);
   1559 	}
   1560 
   1561 	ui.updatePosSections = true;
   1562 	ui.updatePosEdScrollBar = true;
   1563 	setSongModifiedFlag();
   1564 
   1565 	unlockMixerCallback();
   1566 }
   1567 
   1568 void pbPosEdPattUp(void)
   1569 {
   1570 	if (song.orders[song.songPos] == 255)
   1571 		return;
   1572 
   1573 	lockMixerCallback();
   1574 	if (song.orders[song.songPos] < 255)
   1575 	{
   1576 		song.orders[song.songPos]++;
   1577 		song.pattNum = song.orders[song.songPos];
   1578 
   1579 		song.currNumRows = patternNumRows[song.pattNum];
   1580 		if (song.row >= song.currNumRows)
   1581 		{
   1582 			song.row = song.currNumRows-1;
   1583 			if (!songPlaying)
   1584 				editor.row = song.row;
   1585 		}
   1586 
   1587 		if (!songPlaying)
   1588 			editor.editPattern = (uint8_t)song.pattNum;
   1589 
   1590 		checkMarkLimits();
   1591 		ui.updatePatternEditor = true;
   1592 		ui.updatePosSections = true;
   1593 
   1594 		setSongModifiedFlag();
   1595 	}
   1596 	unlockMixerCallback();
   1597 }
   1598 
   1599 void pbPosEdPattDown(void)
   1600 {
   1601 	if (song.orders[song.songPos] == 0)
   1602 		return;
   1603 
   1604 	lockMixerCallback();
   1605 	if (song.orders[song.songPos] > 0)
   1606 	{
   1607 		song.orders[song.songPos]--;
   1608 		song.pattNum = song.orders[song.songPos];
   1609 
   1610 		song.currNumRows = patternNumRows[song.pattNum];
   1611 		if (song.row >= song.currNumRows)
   1612 		{
   1613 			song.row = song.currNumRows-1;
   1614 			if (!songPlaying)
   1615 				editor.row = song.row;
   1616 		}
   1617 
   1618 		if (!songPlaying)
   1619 			editor.editPattern = (uint8_t)song.pattNum;
   1620 
   1621 		checkMarkLimits();
   1622 		ui.updatePatternEditor = true;
   1623 		ui.updatePosSections = true;
   1624 
   1625 		setSongModifiedFlag();
   1626 	}
   1627 	unlockMixerCallback();
   1628 }
   1629 
   1630 void pbPosEdLenUp(void)
   1631 {
   1632 	if (song.songLength >= 255)
   1633 		return;
   1634 
   1635 	const bool audioWasntLocked = !audio.locked;
   1636 	if (audioWasntLocked)
   1637 		lockAudio();
   1638 
   1639 	song.songLength++;
   1640 
   1641 	ui.updatePosSections = true;
   1642 	ui.updatePosEdScrollBar = true;
   1643 	setSongModifiedFlag();
   1644 
   1645 	if (audioWasntLocked)
   1646 		unlockAudio();
   1647 }
   1648 
   1649 void pbPosEdLenDown(void)
   1650 {
   1651 	if (song.songLength <= 1)
   1652 		return;
   1653 
   1654 	const bool audioWasntLocked = !audio.locked;
   1655 	if (audioWasntLocked)
   1656 		lockAudio();
   1657 
   1658 	song.songLength--;
   1659 	if (song.songLoopStart >= song.songLength)
   1660 		song.songLoopStart = song.songLength - 1;
   1661 
   1662 	if (song.songPos >= song.songLength)
   1663 	{
   1664 		song.songPos = song.songLength - 1;
   1665 		setPos(song.songPos, -1, false);
   1666 	}
   1667 
   1668 	ui.updatePosSections = true;
   1669 	ui.updatePosEdScrollBar = true;
   1670 	setSongModifiedFlag();
   1671 
   1672 	if (audioWasntLocked)
   1673 		unlockAudio();
   1674 }
   1675 
   1676 void pbPosEdRepSUp(void)
   1677 {
   1678 	const bool audioWasntLocked = !audio.locked;
   1679 	if (audioWasntLocked)
   1680 		lockAudio();
   1681 
   1682 	if (song.songLoopStart < song.songLength-1)
   1683 	{
   1684 		song.songLoopStart++;
   1685 		ui.updatePosSections = true;
   1686 		setSongModifiedFlag();
   1687 	}
   1688 
   1689 	if (audioWasntLocked)
   1690 		unlockAudio();
   1691 }
   1692 
   1693 void pbPosEdRepSDown(void)
   1694 {
   1695 	const bool audioWasntLocked = !audio.locked;
   1696 	if (audioWasntLocked)
   1697 		lockAudio();
   1698 
   1699 	if (song.songLoopStart > 0)
   1700 	{
   1701 		song.songLoopStart--;
   1702 		ui.updatePosSections = true;
   1703 		setSongModifiedFlag();
   1704 	}
   1705 
   1706 	if (audioWasntLocked)
   1707 		unlockAudio();
   1708 }
   1709 
   1710 void pbBPMUp(void)
   1711 {
   1712 	if (song.BPM == 255)
   1713 		return;
   1714 
   1715 	const bool audioWasntLocked = !audio.locked;
   1716 	if (audioWasntLocked)
   1717 		lockAudio();
   1718 
   1719 	if (song.BPM < 255)
   1720 	{
   1721 		song.BPM++;
   1722 		setMixerBPM(song.BPM);
   1723 
   1724 		// if song is playing, the update is handled in the audio/video sync queue
   1725 		if (!songPlaying)
   1726 		{
   1727 			editor.BPM = song.BPM;
   1728 			drawSongBPM(song.BPM);
   1729 		}
   1730 	}
   1731 
   1732 	if (audioWasntLocked)
   1733 		unlockAudio();
   1734 }
   1735 
   1736 void pbBPMDown(void)
   1737 {
   1738 	if (song.BPM == 32)
   1739 		return;
   1740 
   1741 	const bool audioWasntLocked = !audio.locked;
   1742 	if (audioWasntLocked)
   1743 		lockAudio();
   1744 
   1745 	if (song.BPM > 32)
   1746 	{
   1747 		song.BPM--;
   1748 		setMixerBPM(song.BPM);
   1749 
   1750 		// if song is playing, the update is handled in the audio/video sync queue
   1751 		if (!songPlaying)
   1752 		{
   1753 			editor.BPM = song.BPM;
   1754 			drawSongBPM(editor.BPM);
   1755 		}
   1756 	}
   1757 
   1758 	if (audioWasntLocked)
   1759 		unlockAudio();
   1760 }
   1761 
   1762 void pbSpeedUp(void)
   1763 {
   1764 	if (song.speed == 31)
   1765 		return;
   1766 
   1767 	const bool audioWasntLocked = !audio.locked;
   1768 	if (audioWasntLocked)
   1769 		lockAudio();
   1770 
   1771 	if (song.speed < 31)
   1772 	{
   1773 		song.speed++;
   1774 
   1775 		// if song is playing, the update is handled in the audio/video sync queue
   1776 		if (!songPlaying)
   1777 		{
   1778 			editor.speed = song.speed;
   1779 			drawSongSpeed(editor.speed);
   1780 		}
   1781 	}
   1782 
   1783 	if (audioWasntLocked)
   1784 		unlockAudio();
   1785 }
   1786 
   1787 void pbSpeedDown(void)
   1788 {
   1789 	if (song.speed == 0)
   1790 		return;
   1791 
   1792 	const bool audioWasntLocked = !audio.locked;
   1793 	if (audioWasntLocked)
   1794 		lockAudio();
   1795 
   1796 	if (song.speed > 0)
   1797 	{
   1798 		song.speed--;
   1799 
   1800 		// if song is playing, the update is handled in the audio/video sync queue
   1801 		if (!songPlaying)
   1802 		{
   1803 			editor.speed = song.speed;
   1804 			drawSongSpeed(editor.speed);
   1805 		}
   1806 	}
   1807 
   1808 	if (audioWasntLocked)
   1809 		unlockAudio();
   1810 }
   1811 
   1812 void pbIncAdd(void)
   1813 {
   1814 	if (editor.editRowSkip == 16)
   1815 		editor.editRowSkip = 0;
   1816 	else
   1817 		editor.editRowSkip++;
   1818 
   1819 	drawIDAdd();
   1820 }
   1821 
   1822 void pbDecAdd(void)
   1823 {
   1824 	if (editor.editRowSkip == 0)
   1825 		editor.editRowSkip = 16;
   1826 	else
   1827 		editor.editRowSkip--;
   1828 
   1829 	drawIDAdd();
   1830 }
   1831 
   1832 void pbAddChan(void)
   1833 {
   1834 	if (song.numChannels > 30)
   1835 		return;
   1836 
   1837 	lockMixerCallback();
   1838 	song.numChannels += 2;
   1839 
   1840 	hideTopScreen();
   1841 	showTopLeftMainScreen(true);
   1842 	showTopRightMainScreen();
   1843 
   1844 	if (ui.patternEditorShown)
   1845 		showPatternEditor();
   1846 
   1847 	setSongModifiedFlag();
   1848 	unlockMixerCallback();
   1849 }
   1850 
   1851 void pbSubChan(void)
   1852 {
   1853 	if (song.numChannels < 4)
   1854 		return;
   1855 
   1856 	lockMixerCallback();
   1857 
   1858 	song.numChannels -= 2;
   1859 
   1860 	checkMarkLimits();
   1861 
   1862 	hideTopScreen();
   1863 	showTopLeftMainScreen(true);
   1864 	showTopRightMainScreen();
   1865 
   1866 	if (ui.patternEditorShown)
   1867 		showPatternEditor();
   1868 
   1869 	setSongModifiedFlag();
   1870 	unlockMixerCallback();
   1871 }
   1872 
   1873 void pbEditPattUp(void)
   1874 {
   1875 	const bool audioWasntLocked = !audio.locked;
   1876 	if (audioWasntLocked)
   1877 		lockAudio();
   1878 
   1879 	if (song.pattNum < 255)
   1880 	{
   1881 		song.pattNum++;
   1882 
   1883 		song.currNumRows = patternNumRows[song.pattNum];
   1884 		if (song.row >= song.currNumRows)
   1885 		{
   1886 			song.row = song.currNumRows-1;
   1887 			if (!songPlaying)
   1888 				editor.row = song.row;
   1889 		}
   1890 
   1891 		if (!songPlaying)
   1892 			editor.editPattern = (uint8_t)song.pattNum;
   1893 
   1894 		checkMarkLimits();
   1895 		ui.updatePatternEditor = true;
   1896 		ui.updatePosSections = true;
   1897 	}
   1898 
   1899 	if (audioWasntLocked)
   1900 		unlockAudio();
   1901 }
   1902 
   1903 void pbEditPattDown(void)
   1904 {
   1905 	const bool audioWasntLocked = !audio.locked;
   1906 	if (audioWasntLocked)
   1907 		lockAudio();
   1908 
   1909 	if (song.pattNum > 0)
   1910 	{
   1911 		song.pattNum--;
   1912 
   1913 		song.currNumRows = patternNumRows[song.pattNum];
   1914 		if (song.row >= song.currNumRows)
   1915 		{
   1916 			song.row = song.currNumRows-1;
   1917 			if (!songPlaying)
   1918 				editor.row = song.row;
   1919 		}
   1920 
   1921 		if (!songPlaying)
   1922 			editor.editPattern = (uint8_t)song.pattNum;
   1923 
   1924 		checkMarkLimits();
   1925 		ui.updatePatternEditor = true;
   1926 		ui.updatePosSections = true;
   1927 	}
   1928 
   1929 	if (audioWasntLocked)
   1930 		unlockAudio();
   1931 }
   1932 
   1933 void pbPattLenUp(void)
   1934 {
   1935 	const uint16_t numRows = patternNumRows[editor.editPattern];
   1936 	if (numRows >= MAX_PATT_LEN)
   1937 		return;
   1938 
   1939 	const bool audioWasntLocked = !audio.locked;
   1940 	if (audioWasntLocked)
   1941 		lockAudio();
   1942 	
   1943 	song.pattNum = editor.editPattern; // kludge
   1944 	setPatternLen(editor.editPattern, numRows+1);
   1945 
   1946 	ui.updatePatternEditor = true;
   1947 	ui.updatePosSections = true;
   1948 	setSongModifiedFlag();
   1949 
   1950 	if (audioWasntLocked)
   1951 		unlockAudio();
   1952 }
   1953 
   1954 void pbPattLenDown(void)
   1955 {
   1956 	const uint16_t numRows = patternNumRows[editor.editPattern];
   1957 	if (numRows <= 1)
   1958 		return;
   1959 
   1960 	const bool audioWasntLocked = !audio.locked;
   1961 	if (audioWasntLocked)
   1962 		lockAudio();
   1963 	
   1964 	song.pattNum = editor.editPattern; // kludge
   1965 	setPatternLen(editor.editPattern, numRows-1);
   1966 
   1967 	ui.updatePatternEditor = true;
   1968 	ui.updatePosSections = true;
   1969 	setSongModifiedFlag();
   1970 
   1971 	if (audioWasntLocked)
   1972 		unlockAudio();
   1973 }
   1974 
   1975 void drawPosEdNums(int16_t songPos)
   1976 {
   1977 	if (songPos >= song.songLength)
   1978 		songPos = song.songLength - 1;
   1979 
   1980 	// clear
   1981 	if (ui.extendedPatternEditor)
   1982 	{
   1983 		clearRect(8,  4, 39, 16);
   1984 		fillRect(8, 23, 39, 7, PAL_DESKTOP);
   1985 		clearRect(8, 33, 39, 16);
   1986 	}
   1987 	else
   1988 	{
   1989 		clearRect(8,  4, 39, 15);
   1990 		fillRect(8, 22, 39, 7, PAL_DESKTOP);
   1991 		clearRect(8, 32, 39, 15);
   1992 	}
   1993 
   1994 	const uint32_t color1 = video.palette[PAL_PATTEXT];
   1995 	const uint32_t color2 = video.palette[PAL_FORGRND];
   1996 
   1997 	// top two
   1998 	for (int16_t y = 0; y < 2; y++)
   1999 	{
   2000 		int16_t entry = songPos - (2 - y);
   2001 		if (entry < 0)
   2002 			continue;
   2003 
   2004 		assert(entry < 256);
   2005 
   2006 		if (ui.extendedPatternEditor)
   2007 		{
   2008 			pattTwoHexOut(8,  4 + (y * 9), (uint8_t)entry, color1);
   2009 			pattTwoHexOut(32, 4 + (y * 9), song.orders[entry], color1);
   2010 		}
   2011 		else
   2012 		{
   2013 			pattTwoHexOut(8,  4 + (y * 8), (uint8_t)entry, color1);
   2014 			pattTwoHexOut(32, 4 + (y * 8), song.orders[entry], color1);
   2015 		}
   2016 	}
   2017 
   2018 	assert(songPos < 256);
   2019 
   2020 	// middle
   2021 	if (ui.extendedPatternEditor)
   2022 	{
   2023 		pattTwoHexOut(8,  23, (uint8_t)songPos, color2);
   2024 		pattTwoHexOut(32, 23, song.orders[songPos], color2);
   2025 	}
   2026 	else
   2027 	{
   2028 		pattTwoHexOut(8,  22, (uint8_t)songPos, color2);
   2029 		pattTwoHexOut(32, 22, song.orders[songPos], color2);
   2030 	}
   2031 
   2032 	// bottom two
   2033 	for (int16_t y = 0; y < 2; y++)
   2034 	{
   2035 		int16_t entry = songPos + (1 + y);
   2036 		if (entry >= song.songLength)
   2037 			break;
   2038 
   2039 		if (ui.extendedPatternEditor)
   2040 		{
   2041 			pattTwoHexOut(8,  33 + (y * 9), (uint8_t)entry, color1);
   2042 			pattTwoHexOut(32, 33 + (y * 9), song.orders[entry], color1);
   2043 		}
   2044 		else
   2045 		{
   2046 			pattTwoHexOut(8,  32 + (y * 8), (uint8_t)entry, color1);
   2047 			pattTwoHexOut(32, 32 + (y * 8), song.orders[entry], color1);
   2048 		}
   2049 	}
   2050 }
   2051 
   2052 void drawSongLength(void)
   2053 {
   2054 	int16_t x, y;
   2055 
   2056 	if (ui.extendedPatternEditor)
   2057 	{
   2058 		x = 165;
   2059 		y = 5;
   2060 	}
   2061 	else
   2062 	{
   2063 		x = 59;
   2064 		y = 52;
   2065 	}
   2066 
   2067 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLength, 2);
   2068 }
   2069 
   2070 void drawSongLoopStart(void)
   2071 {
   2072 	int16_t x, y;
   2073 
   2074 	if (ui.extendedPatternEditor)
   2075 	{
   2076 		x = 165;
   2077 		y = 19;
   2078 	}
   2079 	else
   2080 	{
   2081 		x = 59;
   2082 		y = 64;
   2083 	}
   2084 
   2085 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLoopStart, 2);
   2086 }
   2087 
   2088 void drawSongBPM(uint16_t val)
   2089 {
   2090 	if (ui.extendedPatternEditor)
   2091 		return;
   2092 
   2093 	if (val > 255)
   2094 		val = 255;
   2095 
   2096 	textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[val]);
   2097 }
   2098 
   2099 void drawSongSpeed(uint16_t val)
   2100 {
   2101 	if (ui.extendedPatternEditor)
   2102 		return;
   2103 
   2104 	if (val > 99)
   2105 		val = 99;
   2106 
   2107 	textOutFixed(152, 50, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
   2108 }
   2109 
   2110 void drawEditPattern(uint16_t editPattern)
   2111 {
   2112 	int16_t x, y;
   2113 
   2114 	if (ui.extendedPatternEditor)
   2115 	{
   2116 		x = 252;
   2117 		y = 39;
   2118 	}
   2119 	else
   2120 	{
   2121 		x = 237;
   2122 		y = 36;
   2123 	}
   2124 
   2125 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, editPattern, 2);
   2126 }
   2127 
   2128 void drawPatternLength(uint16_t editPattern)
   2129 {
   2130 	int16_t x, y;
   2131 
   2132 	if (ui.extendedPatternEditor)
   2133 	{
   2134 		x = 326;
   2135 		y = 39;
   2136 	}
   2137 	else
   2138 	{
   2139 		x = 230;
   2140 		y = 50;
   2141 	}
   2142 
   2143 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, patternNumRows[editPattern], 3);
   2144 }
   2145 
   2146 void drawGlobalVol(uint16_t val)
   2147 {
   2148 	uint16_t x = 87, y = 80;
   2149 
   2150 	if (ui.extendedPatternEditor)
   2151 		y = 56;
   2152 
   2153 	assert(val <= 64);
   2154 	textOutFixed(x, y, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
   2155 }
   2156 
   2157 void drawIDAdd(void)
   2158 {
   2159 	assert(editor.editRowSkip <= 16);
   2160 	textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.editRowSkip]);
   2161 }
   2162 
   2163 void resetPlaybackTime(void)
   2164 {
   2165 	song.playbackSeconds = 0;
   2166 	song.playbackSecondsFrac = 0;
   2167 
   2168 	last_TimeH = 0;
   2169 	last_TimeM = 0;
   2170 	last_TimeS = 0;
   2171 }
   2172 
   2173 void drawPlaybackTime(void)
   2174 {
   2175 	if (songPlaying)
   2176 	{
   2177 		uint32_t seconds = song.playbackSeconds;
   2178 
   2179 		last_TimeH = seconds / 3600;
   2180 		seconds -= last_TimeH * 3600;
   2181 
   2182 		last_TimeM = seconds / 60;
   2183 		seconds -= last_TimeM * 60;
   2184 
   2185 		last_TimeS = seconds;
   2186 	}
   2187 
   2188 	uint16_t x = 235, y = 80;
   2189 
   2190 	if (ui.extendedPatternEditor)
   2191 	{
   2192 		x = 576;
   2193 		y = 56;
   2194 	}
   2195 
   2196 	textOutFixed(x+0, y, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeH]);
   2197 	textOutFixed(x+20, y, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeM]);
   2198 	textOutFixed(x+40, y, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeS]);
   2199 }
   2200 
   2201 void drawSongName(void)
   2202 {
   2203 	drawFramework(421, 155, 166, 18, FRAMEWORK_TYPE1);
   2204 	drawFramework(423, 157, 162, 14, FRAMEWORK_TYPE2);
   2205 	drawTextBox(TB_SONG_NAME);
   2206 }
   2207 
   2208 void changeLogoType(uint8_t logoType)
   2209 {
   2210 	pushButtons[PB_LOGO].bitmapFlag = true;
   2211 
   2212 	if (logoType == 0)
   2213 	{
   2214 		pushButtons[PB_LOGO].bitmapUnpressed = &bmp.ft2LogoBadges[(154 * 32) * 0];
   2215 		pushButtons[PB_LOGO].bitmapPressed = &bmp.ft2LogoBadges[(154 * 32) * 1];
   2216 	}
   2217 	else
   2218 	{
   2219 		pushButtons[PB_LOGO].bitmapUnpressed = &bmp.ft2LogoBadges[(154 * 32) * 2];
   2220 		pushButtons[PB_LOGO].bitmapPressed = &bmp.ft2LogoBadges[(154 * 32) * 3];
   2221 	}
   2222 
   2223 	drawPushButton(PB_LOGO);
   2224 }
   2225 
   2226 void changeBadgeType(uint8_t badgeType)
   2227 {
   2228 	pushButtons[PB_BADGE].bitmapFlag = true;
   2229 
   2230 	if (badgeType == 0)
   2231 	{
   2232 		pushButtons[PB_BADGE].bitmapUnpressed = &bmp.ft2ByBadges[(25 * 32) * 0];
   2233 		pushButtons[PB_BADGE].bitmapPressed = &bmp.ft2ByBadges[(25 * 32) * 1];
   2234 	}
   2235 	else
   2236 	{
   2237 		pushButtons[PB_BADGE].bitmapUnpressed = &bmp.ft2ByBadges[(25 * 32) * 2];
   2238 		pushButtons[PB_BADGE].bitmapPressed = &bmp.ft2ByBadges[(25 * 32) * 3];
   2239 	}
   2240 
   2241 	drawPushButton(PB_BADGE);
   2242 }
   2243 
   2244 void updateInstrumentSwitcher(void)
   2245 {
   2246 	int16_t y;
   2247 
   2248 	if (ui.aboutScreenShown || ui.configScreenShown || ui.helpScreenShown || ui.nibblesShown)
   2249 		return; // don't redraw instrument switcher when it's not shown!
   2250 
   2251 	if (ui.extendedPatternEditor) // extended pattern editor
   2252 	{
   2253 		//INSTRUMENTS
   2254 
   2255 		clearRect(388, 5, 116, 43); // left box
   2256 		clearRect(511, 5, 116, 43); // right box
   2257 
   2258 		// draw source instrument selection
   2259 		if (editor.srcInstr >= editor.instrBankOffset && editor.srcInstr <= editor.instrBankOffset+8)
   2260 		{
   2261 			y = 5 + ((editor.srcInstr - editor.instrBankOffset - 1) * 11);
   2262 			if (y >= 5 && y <= 82)
   2263 			{
   2264 				if (y <= 47)
   2265 					fillRect(388, y, 15, 10, PAL_BUTTONS); // left box
   2266 				else
   2267 					fillRect(511, y - 44, 15, 10, PAL_BUTTONS); // right box
   2268 			}
   2269 		}
   2270 
   2271 		// draw destination instrument selection
   2272 		if (editor.curInstr >= editor.instrBankOffset && editor.curInstr <= editor.instrBankOffset+8)
   2273 		{
   2274 			y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
   2275 			y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
   2276 			if (y >= 5 && y <= 82)
   2277 			{
   2278 				if (y <= 47)
   2279 					fillRect(406, y, 98, 10, PAL_BUTTONS); // left box
   2280 				else
   2281 					fillRect(529, y - 44, 98, 10, PAL_BUTTONS); // right box
   2282 			}
   2283 		}
   2284 
   2285 		// draw numbers and texts
   2286 		for (int16_t i = 0; i < 4; i++)
   2287 		{
   2288 			hexOut(388, 5 + (i * 11), PAL_FORGRND, 1 + editor.instrBankOffset + i, 2);
   2289 			hexOut(511, 5 + (i * 11), PAL_FORGRND, 5 + editor.instrBankOffset + i, 2);
   2290 			drawTextBox(TB_INST1 + i);
   2291 			drawTextBox(TB_INST5 + i);
   2292 		}
   2293 	}
   2294 	else // normal pattern editor
   2295 	{
   2296 		// INSTRUMENTS
   2297 
   2298 		clearRect(424, 5,  15, 87); // src instrument
   2299 		clearRect(446, 5, 139, 87); // main instrument
   2300 
   2301 		// draw source instrument selection
   2302 		if (editor.srcInstr >= editor.instrBankOffset && editor.srcInstr <= editor.instrBankOffset+8)
   2303 		{
   2304 			y = 5 + ((editor.srcInstr - editor.instrBankOffset - 1) * 11);
   2305 			if (y >= 5 && y <= 82)
   2306 				fillRect(424, y, 15, 10, PAL_BUTTONS);
   2307 		}
   2308 
   2309 		// draw destination instrument selection
   2310 		if (editor.curInstr >= editor.instrBankOffset && editor.curInstr <= editor.instrBankOffset+8)
   2311 		{
   2312 			y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
   2313 			if (y >= 5 && y <= 82)
   2314 				fillRect(446, y, 139, 10, PAL_BUTTONS);
   2315 		}
   2316 
   2317 		// draw numbers and texts
   2318 		for (int16_t i = 0; i < 8; i++)
   2319 		{
   2320 			hexOut(424, 5 + (i * 11), PAL_FORGRND, 1 + editor.instrBankOffset + i, 2);
   2321 			drawTextBox(TB_INST1 + i);
   2322 		}
   2323 
   2324 		// SAMPLES
   2325 
   2326 		clearRect(424, 99,  15, 54); // src sample
   2327 		clearRect(446, 99, 115, 54); // main sample
   2328 
   2329 		// draw source sample selection
   2330 		if (editor.srcSmp >= editor.sampleBankOffset && editor.srcSmp <= editor.sampleBankOffset+4)
   2331 		{
   2332 			y = 99 + ((editor.srcSmp - editor.sampleBankOffset) * 11);
   2333 			if (y >= 36 && y <= 143)
   2334 				fillRect(424, y, 15, 10, PAL_BUTTONS);
   2335 		}
   2336 
   2337 		// draw destination sample selection
   2338 		if (editor.curSmp >= editor.sampleBankOffset && editor.curSmp <= editor.sampleBankOffset+4)
   2339 		{
   2340 			y = 99 + ((editor.curSmp - editor.sampleBankOffset) * 11);
   2341 			if (y >= 36 && y <= 143)
   2342 				fillRect(446, y, 115, 10, PAL_BUTTONS);
   2343 		}
   2344 
   2345 		// draw numbers and texts
   2346 		for (int16_t i = 0; i < 5; i++)
   2347 		{
   2348 			hexOut(424, 99 + (i * 11), PAL_FORGRND, editor.sampleBankOffset + i, 2);
   2349 			drawTextBox(TB_SAMP1 + i);
   2350 		}
   2351 	}
   2352 }
   2353 
   2354 void showInstrumentSwitcher(void)
   2355 {
   2356 	if (!ui.instrSwitcherShown)
   2357 		return;
   2358 
   2359 	for (uint16_t i = 0; i < 8; i++)
   2360 		showTextBox(TB_INST1 + i);
   2361 
   2362 	if (ui.extendedPatternEditor)
   2363 	{
   2364 		hidePushButton(PB_SAMPLE_LIST_UP);
   2365 		hidePushButton(PB_SAMPLE_LIST_DOWN);
   2366 		hideScrollBar(SB_SAMPLE_LIST);
   2367 
   2368 		drawFramework(386,  0, 246,   3, FRAMEWORK_TYPE1);
   2369 		drawFramework(506,  3,   3,  47, FRAMEWORK_TYPE1);
   2370 		drawFramework(386, 50, 246,   3, FRAMEWORK_TYPE1);
   2371 		drawFramework(629,  3,   3,  47, FRAMEWORK_TYPE1);
   2372 
   2373 		clearRect(386, 3, 120, 47);
   2374 		clearRect(509, 3, 120, 47);
   2375 	}
   2376 	else
   2377 	{
   2378 		drawFramework(421,   0, 166,   3, FRAMEWORK_TYPE1);
   2379 		drawFramework(442,   3,   3,  91, FRAMEWORK_TYPE1);
   2380 		drawFramework(421,  94, 166,   3, FRAMEWORK_TYPE1);
   2381 		drawFramework(442,  97,   3,  58, FRAMEWORK_TYPE1);
   2382 		drawFramework(563,  97,  24,  58, FRAMEWORK_TYPE1);
   2383 		drawFramework(587,   0,  45,  71, FRAMEWORK_TYPE1);
   2384 		drawFramework(587,  71,  45,  71, FRAMEWORK_TYPE1);
   2385 		drawFramework(587, 142,  45,  31, FRAMEWORK_TYPE1);
   2386 
   2387 		fillRect(421,  3,  21, 91, PAL_BCKGRND);
   2388 		fillRect(445,  3, 142, 91, PAL_BCKGRND);
   2389 		fillRect(421, 97,  21, 58, PAL_BCKGRND);
   2390 		fillRect(445, 97, 118, 58, PAL_BCKGRND);
   2391 
   2392 		showPushButton(PB_SAMPLE_LIST_UP);
   2393 		showPushButton(PB_SAMPLE_LIST_DOWN);
   2394 		showScrollBar(SB_SAMPLE_LIST);
   2395 
   2396 		for (uint16_t i = 0; i < 5; i++)
   2397 			showTextBox(TB_SAMP1 + i);
   2398 	}
   2399 
   2400 	updateInstrumentSwitcher();
   2401 
   2402 	for (uint16_t i = 0; i < 8; i++)
   2403 		showPushButton(PB_RANGE1 + i + (editor.instrBankSwapped * 8));
   2404 
   2405 	showPushButton(PB_SWAP_BANK);
   2406 }
   2407 
   2408 void hideInstrumentSwitcher(void)
   2409 {
   2410 	for (uint16_t i = 0; i < 16; i++)
   2411 		hidePushButton(PB_RANGE1 + i);
   2412 
   2413 	hidePushButton(PB_SWAP_BANK);
   2414 	hidePushButton(PB_SAMPLE_LIST_UP);
   2415 	hidePushButton(PB_SAMPLE_LIST_DOWN);
   2416 	hideScrollBar(SB_SAMPLE_LIST);
   2417 
   2418 	for (uint16_t i = 0; i < 8; i++)
   2419 		hideTextBox(TB_INST1 + i);
   2420 
   2421 	for (uint16_t i = 0; i < 5; i++)
   2422 		hideTextBox(TB_SAMP1 + i);
   2423 }
   2424 
   2425 void pbSwapInstrBank(void)
   2426 {
   2427 	editor.instrBankSwapped ^= 1;
   2428 
   2429 	if (editor.instrBankSwapped)
   2430 		editor.instrBankOffset += 8*8;
   2431 	else
   2432 		editor.instrBankOffset -= 8*8;
   2433 
   2434 	updateTextBoxPointers();
   2435 
   2436 	if (ui.instrSwitcherShown)
   2437 	{
   2438 		updateInstrumentSwitcher();
   2439 		for (uint16_t i = 0; i < 8; i++)
   2440 		{
   2441 			hidePushButton(PB_RANGE1 + i + (!editor.instrBankSwapped * 8));
   2442 			showPushButton(PB_RANGE1 + i + ( editor.instrBankSwapped * 8));
   2443 		}
   2444 	}
   2445 }
   2446 
   2447 void pbSetInstrBank1(void)
   2448 {
   2449 	editor.instrBankOffset = 0 * 8;
   2450 
   2451 	updateTextBoxPointers();
   2452 	updateInstrumentSwitcher();
   2453 }
   2454 
   2455 void pbSetInstrBank2(void)
   2456 {
   2457 	editor.instrBankOffset = 1 * 8;
   2458 
   2459 	updateTextBoxPointers();
   2460 	updateInstrumentSwitcher();
   2461 }
   2462 
   2463 void pbSetInstrBank3(void)
   2464 {
   2465 	editor.instrBankOffset = 2 * 8;
   2466 
   2467 	updateTextBoxPointers();
   2468 	updateInstrumentSwitcher();
   2469 }
   2470 
   2471 void pbSetInstrBank4(void)
   2472 {
   2473 	editor.instrBankOffset = 3 * 8;
   2474 
   2475 	updateTextBoxPointers();
   2476 	updateInstrumentSwitcher();
   2477 }
   2478 
   2479 void pbSetInstrBank5(void)
   2480 {
   2481 	editor.instrBankOffset = 4 * 8;
   2482 
   2483 	updateTextBoxPointers();
   2484 	updateInstrumentSwitcher();
   2485 }
   2486 
   2487 void pbSetInstrBank6(void)
   2488 {
   2489 	editor.instrBankOffset = 5 * 8;
   2490 
   2491 	updateTextBoxPointers();
   2492 	updateInstrumentSwitcher();
   2493 }
   2494 
   2495 void pbSetInstrBank7(void)
   2496 {
   2497 	editor.instrBankOffset = 6 * 8;
   2498 
   2499 	updateTextBoxPointers();
   2500 	updateInstrumentSwitcher();
   2501 }
   2502 
   2503 void pbSetInstrBank8(void)
   2504 {
   2505 	editor.instrBankOffset = 7 * 8;
   2506 
   2507 	updateTextBoxPointers();
   2508 	updateInstrumentSwitcher();
   2509 }
   2510 
   2511 void pbSetInstrBank9(void)
   2512 {
   2513 	editor.instrBankOffset = 8 * 8;
   2514 
   2515 	updateTextBoxPointers();
   2516 	updateInstrumentSwitcher();
   2517 }
   2518 
   2519 void pbSetInstrBank10(void)
   2520 {
   2521 	editor.instrBankOffset = 9 * 8;
   2522 
   2523 	updateTextBoxPointers();
   2524 	updateInstrumentSwitcher();
   2525 }
   2526 
   2527 void pbSetInstrBank11(void)
   2528 {
   2529 	editor.instrBankOffset = 10 * 8;
   2530 
   2531 	updateTextBoxPointers();
   2532 	updateInstrumentSwitcher();
   2533 }
   2534 
   2535 void pbSetInstrBank12(void)
   2536 {
   2537 	editor.instrBankOffset = 11 * 8;
   2538 
   2539 	updateTextBoxPointers();
   2540 	updateInstrumentSwitcher();
   2541 }
   2542 
   2543 void pbSetInstrBank13(void)
   2544 {
   2545 	editor.instrBankOffset = 12 * 8;
   2546 
   2547 	updateTextBoxPointers();
   2548 	updateInstrumentSwitcher();
   2549 }
   2550 
   2551 void pbSetInstrBank14(void)
   2552 {
   2553 	editor.instrBankOffset = 13 * 8;
   2554 
   2555 	updateTextBoxPointers();
   2556 	updateInstrumentSwitcher();
   2557 }
   2558 
   2559 void pbSetInstrBank15(void)
   2560 {
   2561 	editor.instrBankOffset = 14 * 8;
   2562 
   2563 	updateTextBoxPointers();
   2564 	updateInstrumentSwitcher();
   2565 }
   2566 
   2567 void pbSetInstrBank16(void)
   2568 {
   2569 	editor.instrBankOffset = 15 * 8;
   2570 
   2571 	updateTextBoxPointers();
   2572 	updateInstrumentSwitcher();
   2573 }
   2574 
   2575 void setNewInstr(int16_t ins)
   2576 { 
   2577 	if (ins <= MAX_INST)
   2578 	{
   2579 		editor.curInstr = (uint8_t)ins;
   2580 		updateTextBoxPointers();
   2581 		updateInstrumentSwitcher();
   2582 		updateNewInstrument();
   2583 	}
   2584 }
   2585 
   2586 void sampleListScrollUp(void)
   2587 {
   2588 	scrollBarScrollUp(SB_SAMPLE_LIST, 1);
   2589 }
   2590 
   2591 void sampleListScrollDown(void)
   2592 {
   2593 	scrollBarScrollDown(SB_SAMPLE_LIST, 1);
   2594 }
   2595 
   2596 static void zapSong(void)
   2597 {
   2598 	lockMixerCallback();
   2599 
   2600 	song.songLength = 1;
   2601 	song.songLoopStart = 0; // FT2 doesn't do this!
   2602 	song.BPM = 125;
   2603 	song.speed = 6;
   2604 	song.songPos = 0;
   2605 	song.globalVolume = 64;
   2606 
   2607 	memset(song.name, 0, sizeof (song.name));
   2608 	memset(song.orders, 0, sizeof (song.orders));
   2609 
   2610 	// zero all pattern data and reset pattern lengths
   2611 
   2612 	freeAllPatterns();
   2613 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
   2614 		patternNumRows[i] = 64;
   2615 	song.currNumRows = patternNumRows[song.pattNum];
   2616 
   2617 	resetMusic();
   2618 	setMixerBPM(song.BPM);
   2619 
   2620 	editor.songPos = song.songPos;
   2621 	editor.editPattern = song.pattNum;
   2622 	editor.BPM = song.BPM;
   2623 	editor.speed = song.speed;
   2624 	editor.globalVolume = song.globalVolume;
   2625 	editor.tick = 1;
   2626 
   2627 	resetPlaybackTime();
   2628 
   2629 	if (!audio.linearPeriodsFlag)
   2630 		setLinearPeriods(true);
   2631 
   2632 	clearPattMark();
   2633 	resetWavRenderer();
   2634 	resetChannels();
   2635 	unlockMixerCallback();
   2636 
   2637 	setScrollBarPos(SB_POS_ED, 0, false);
   2638 	setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
   2639 
   2640 	updateWindowTitle(true);
   2641 }
   2642 
   2643 static void zapInstrs(void)
   2644 {
   2645 	lockMixerCallback();
   2646 
   2647 	for (int16_t i = 1; i <= MAX_INST; i++)
   2648 	{
   2649 		freeInstr(i);
   2650 		memset(song.instrName[i], 0, 22+1);
   2651 	}
   2652 
   2653 	updateNewInstrument();
   2654 
   2655 	editor.currVolEnvPoint = 0;
   2656 	editor.currPanEnvPoint = 0;
   2657 
   2658 	updateSampleEditorSample();
   2659 
   2660 	if (ui.sampleEditorShown)
   2661 		updateSampleEditor();
   2662 	else if (ui.instEditorShown || ui.instEditorExtShown)
   2663 		updateInstEditor();
   2664 
   2665 	unlockMixerCallback();
   2666 }
   2667 
   2668 void pbZap(void)
   2669 {
   2670 	const int16_t choice = okBox(3, "System request", "Total devastation of the...", NULL);
   2671 
   2672 	if (choice == 1) // zap all
   2673 	{
   2674 		zapSong();
   2675 		zapInstrs();
   2676 	}
   2677 	else if (choice == 2) // zap song
   2678 	{
   2679 		zapSong();
   2680 	}
   2681 	else if (choice == 3) // zap instruments
   2682 	{
   2683 		zapInstrs();
   2684 	}
   2685 
   2686 	if (choice >= 1 && choice <= 3)
   2687 	{
   2688 		// redraw top screens
   2689 		hideTopScreen();
   2690 		showTopScreen(true);
   2691 
   2692 		setSongModifiedFlag();
   2693 	}
   2694 }
   2695 
   2696 void sbSmpBankPos(uint32_t pos)
   2697 {
   2698 	if (editor.sampleBankOffset != pos)
   2699 	{
   2700 		editor.sampleBankOffset = (uint8_t)pos;
   2701 
   2702 		updateTextBoxPointers();
   2703 		updateInstrumentSwitcher();
   2704 	}
   2705 }
   2706 
   2707 void pbToggleLogo(void)
   2708 {
   2709 	config.id_FastLogo ^= 1;
   2710 	changeLogoType(config.id_FastLogo);
   2711 }
   2712 
   2713 void pbToggleBadge(void)
   2714 {
   2715 	config.id_TritonProd ^= 1;
   2716 	changeBadgeType(config.id_TritonProd);
   2717 }
   2718 
   2719 void resetChannelOffset(void)
   2720 {
   2721 	ui.pattChanScrollShown = song.numChannels > getMaxVisibleChannels();
   2722 	cursor.object = CURSOR_NOTE;
   2723 	cursor.ch = 0;
   2724 	setScrollBarPos(SB_CHAN_SCROLL, 0, true);
   2725 	ui.channelOffset = 0;
   2726 }
   2727 
   2728 void shrinkPattern(void)
   2729 {
   2730 	pauseMusic();
   2731 	const volatile uint16_t curPattern = editor.editPattern;
   2732 	int16_t numRows = patternNumRows[editor.editPattern];
   2733 	resumeMusic();
   2734 
   2735 	if (numRows <= 1)
   2736 	{
   2737 		okBox(0, "System message", "Pattern is too short to be shrunk!", NULL);
   2738 		return;
   2739 	}
   2740 
   2741 	if (okBox(2, "System request", "Shrink pattern?", NULL) != 1)
   2742 		return;
   2743 
   2744 	lockMixerCallback();
   2745 
   2746 	note_t *p = pattern[curPattern];
   2747 	if (p != NULL)
   2748 	{
   2749 		for (int32_t i = 0; i < numRows / 2; i++)
   2750 		{
   2751 			for (int32_t j = 0; j < MAX_CHANNELS; j++)
   2752 				p[(i * MAX_CHANNELS) + j] = p[((i*2) * MAX_CHANNELS) + j];
   2753 		}
   2754 	}
   2755 
   2756 	patternNumRows[curPattern] /= 2;
   2757 	numRows = patternNumRows[curPattern];
   2758 
   2759 	if (song.pattNum == curPattern)
   2760 		song.currNumRows = numRows;
   2761 
   2762 	song.row /= 2;
   2763 	if (song.row >= numRows)
   2764 		song.row = numRows-1;
   2765 
   2766 	editor.row = song.row;
   2767 
   2768 	ui.updatePatternEditor = true;
   2769 	ui.updatePosSections = true;
   2770 
   2771 	unlockMixerCallback();
   2772 	setSongModifiedFlag();
   2773 }
   2774 
   2775 void expandPattern(void)
   2776 {
   2777 	pauseMusic();
   2778 	const volatile uint16_t curPattern = editor.editPattern;
   2779 	int16_t numRows = patternNumRows[editor.editPattern];
   2780 	resumeMusic();
   2781 
   2782 	if (numRows > MAX_PATT_LEN/2)
   2783 	{
   2784 		okBox(0, "System message", "Pattern is too long to be expanded!", NULL);
   2785 		return;
   2786 	}
   2787 
   2788 	lockMixerCallback();
   2789 
   2790 	note_t *p = pattern[curPattern];
   2791 	if (p != NULL)
   2792 	{
   2793 		memcpy(tmpPattern, p, numRows * TRACK_WIDTH);
   2794 
   2795 		for (int32_t i = 0; i < numRows; i++)
   2796 		{
   2797 			for (int32_t j = 0; j < MAX_CHANNELS; j++)
   2798 				p[((i * 2) * MAX_CHANNELS) + j] = tmpPattern[(i * MAX_CHANNELS) + j];
   2799 
   2800 			memset(&p[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH);
   2801 		}
   2802 	}
   2803 	
   2804 	patternNumRows[curPattern] *= 2;
   2805 	numRows = patternNumRows[curPattern];
   2806 
   2807 	if (song.pattNum == curPattern)
   2808 		song.currNumRows = numRows;
   2809 
   2810 	song.row *= 2;
   2811 	if (song.row >= numRows)
   2812 		song.row = numRows-1;
   2813 
   2814 	editor.row = song.row;
   2815 
   2816 	ui.updatePatternEditor = true;
   2817 	ui.updatePosSections = true;
   2818 
   2819 	unlockMixerCallback();
   2820 	setSongModifiedFlag();
   2821 }