1 /*
2  *  tracker/Tracker.cpp
3  *
4  *  Copyright 2009 Peter Barth
5  *
6  *  This file is part of Milkytracker.
7  *
8  *  Milkytracker is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Milkytracker is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "Tracker.h"
24 #include "TrackerConfig.h"
25 #include "TabManager.h"
26 #include "PlayerController.h"
27 #include "PlayerMaster.h"
28 #include "PlayerLogic.h"
29 #include "RecorderLogic.h"
30 #include "SamplePlayer.h"
31 #include "SimpleVector.h"
32 #include "ModuleEditor.h"
33 #include "TabTitleProvider.h"
34 #include "PPUI.h"
35 #include "PatternTools.h"
36 #include "PatternEditorControl.h"
37 #include "EnvelopeEditorControl.h"
38 #include "PianoControl.h"
39 #include "PeakLevelControl.h"
40 #include "ScopesControl.h"
41 #include "TabHeaderControl.h"
42 #include "SampleEditorControl.h"
43 #include "TrackerSettingsDatabase.h"
44 #include "KeyBindings.h"
45 #include "ModuleServices.h"
46 #include "FileIdentificator.h"
47 #include "FileExtProvider.h"
48 #include "Decompressor.h"
49 #include "Zapper.h"
50 #include "TitlePageManager.h"
51 
52 // Sections
53 #include "SectionSwitcher.h"
54 #include "SectionTranspose.h"
55 #include "SectionAdvancedEdit.h"
56 #include "SectionDiskMenu.h"
57 #include "SectionHDRecorder.h"
58 #include "SectionSettings.h"
59 #include "SectionInstruments.h"
60 #include "SectionSamples.h"
61 #include "SectionQuickOptions.h"
62 #include "SectionOptimize.h"
63 #include "SectionAbout.h"
64 
65 #include "InputControlListener.h"
66 
67 // Some helper messageboxes & button handlers
68 #include "DialogHandlers.h"
69 #include "DialogChannelSelector.h"
70 #include "DialogZap.h"
71 // Helper class to invoke tools which need parameters
72 #include "ToolInvokeHelper.h"
73 
74 #include "ControlIDs.h"
75 
76 // OS Interface
77 #include "PPOpenPanel.h"
78 #include "PPSavePanel.h"
79 
myMod(pp_int32 a,pp_int32 b)80 static inline pp_int32 myMod(pp_int32 a, pp_int32 b)
81 {
82 	pp_int32 res = a%b;
83 	if (res<0) res+=b;
84 	return res;
85 }
86 
87 #ifndef __LOWRES__
SCOPESHEIGHT()88 pp_int32 Tracker::SCOPESHEIGHT()
89 {
90 	return (48*screen->getHeight()) / 480;
91 }
92 
CURRENTSCOPESHEIGHT()93 pp_int32 Tracker::CURRENTSCOPESHEIGHT()
94 {
95 	if (!scopesControl)
96 		return 0;
97 
98 	if (scopesControl->isVisible())
99 		return SCOPESHEIGHT();
100 
101 	return 0;
102 }
103 
SAMPLESECTIONDEFAULTHEIGHT()104 pp_int32 Tracker::SAMPLESECTIONDEFAULTHEIGHT()
105 {
106 	return screen->getHeight() < 480 ? 180 : 240;
107 }
108 
109 #endif
110 
MAXEDITORHEIGHT()111 pp_int32 Tracker::MAXEDITORHEIGHT()
112 {
113 #ifndef __LOWRES__
114 	TabHeaderControl* tabControl = static_cast<TabHeaderControl*>(screen->getControlByID(TABHEADER_CONTROL));
115 	if (tabControl != NULL)
116 	{
117 		return tabControl->getNumTabs() > 1 ? screen->getHeight() - TABHEADERHEIGHT() : screen->getHeight();
118 	}
119 #endif
120 	return screen->getHeight();
121 }
122 
Tracker()123 Tracker::Tracker() :
124 	screen(NULL),
125 	peakLevelControl(NULL),
126 	scopesControl(NULL),
127 	messageBoxContainerGeneric(NULL),
128 	dialog(NULL),
129 	responder(NULL),
130 	playTimeText(NULL),
131 	instrumentChooser(NULL),
132 	inputContainerCurrent(NULL),
133 	inputContainerDefault(NULL),
134 	inputContainerExtended(NULL),
135 	settingsDatabaseCopy(NULL),
136 	eventKeyDownBindings(NULL),
137 	eventKeyDownBindingsMilkyTracker(NULL),
138 	eventKeyDownBindingsFastTracker(NULL),
139 	currentFileName(TrackerConfig::untitledSong),
140 	lastState(false),
141 	editMode(EditModeFastTracker),
142 	extendedOrderlist(false),
143 	followSong(true),
144 	caughtMouseInUpperLeftCorner(false),
145 	useClassicBrowser(false),
146 	savePanel(NULL),
147 	fileSystemChangedListener(NULL)
148 {
149 	resetStateMemories();
150 
151 	settingsDatabase = new TrackerSettingsDatabase();
152 
153 	buildDefaultSettings();
154 
155 	tabManager = new TabManager(*this);
156 
157 	playerMaster = new PlayerMaster(TrackerConfig::numTabs);
158 	playerController = tabManager->createPlayerController();
159 
160 	moduleEditor = tabManager->createModuleEditor();
161 
162 	playerLogic = new PlayerLogic(*this);
163 	recorderLogic = new RecorderLogic(*this);
164 
165 	// Sections
166 	sectionSwitcher = new SectionSwitcher(*this);
167 
168 	sections = new PPSimpleVector<SectionAbstract>();
169 
170 	sectionTranspose = new SectionTranspose(*this);
171 	sections->add(sectionTranspose);
172 	sectionAdvancedEdit = new SectionAdvancedEdit(*this);
173 	sections->add(sectionAdvancedEdit);
174 	sectionDiskMenu = new SectionDiskMenu(*this);
175 	sections->add(sectionDiskMenu);
176 	sectionHDRecorder = new SectionHDRecorder(*this);
177 	sections->add(sectionHDRecorder);
178 	sectionSettings = new SectionSettings(*this);
179 	sections->add(sectionSettings);
180 	sectionInstruments = new SectionInstruments(*this);
181 	sections->add(sectionInstruments);
182 	sectionSamples = new SectionSamples(*this);
183 	sections->add(sectionSamples);
184 	sectionQuickOptions = new SectionQuickOptions(*this);
185 	sections->add(sectionQuickOptions);
186 	sectionOptimize = new SectionOptimize(*this);
187 	sections->add(sectionOptimize);
188 	sectionAbout = new SectionAbout(*this);
189 	sections->add(sectionAbout);
190 
191 	inputControlListener = new InputControlListener(*this);
192 
193 	toolInvokeHelper = new ToolInvokeHelper(*this);
194 
195 	pp_int32 i;
196 
197 	muteChannels = new bool[TrackerConfig::maximumPlayerChannels];
198 
199 	for (i = 0; i < TrackerConfig::maximumPlayerChannels; i++)
200 		muteChannels[i] = false;
201 
202 	initKeyBindings();
203 }
204 
~Tracker()205 Tracker::~Tracker()
206 {
207 	delete eventKeyDownBindingsMilkyTracker;
208 	delete eventKeyDownBindingsFastTracker;
209 
210 	delete toolInvokeHelper;
211 	delete responder;
212 	delete dialog;
213 	delete inputControlListener;
214 
215 	delete sections;
216 	delete sectionSwitcher;
217 
218 	delete recorderLogic;
219 	delete playerLogic;
220 
221 	delete playerMaster;
222 
223 	delete messageBoxContainerGeneric;
224 
225 	delete[] muteChannels;
226 
227 	delete instrumentChooser;
228 
229 	delete settingsDatabaseCopy;
230 	delete settingsDatabase;
231 }
232 
getPatternEditor()233 PatternEditor* Tracker::getPatternEditor()
234 {
235 	return moduleEditor->getPatternEditor();
236 }
237 
getSampleEditor()238 SampleEditor* Tracker::getSampleEditor()
239 {
240 	return moduleEditor->getSampleEditor();
241 }
242 
getEnvelopeEditor()243 EnvelopeEditor* Tracker::getEnvelopeEditor()
244 {
245 	return moduleEditor->getEnvelopeEditor();
246 }
247 
getOrderListBoxIndex()248 pp_int32 Tracker::getOrderListBoxIndex()
249 {
250 	return listBoxOrderList->getSelectedIndex();
251 }
252 
setOrderListIndex(pp_int32 index)253 void Tracker::setOrderListIndex(pp_int32 index)
254 {
255 	// do not accept values which exceed the current number of orders
256 	if (index >= moduleEditor->getNumOrders())
257 		index = moduleEditor->getNumOrders()-1;
258 
259 	listBoxOrderList->setSelectedIndex(index);
260 	updateOrderlist();
261 	// fake selection from orderlist, so everything will be updated correctly
262 	PPEvent e(eSelection, &index, sizeof(index));
263 	handleEvent(reinterpret_cast<PPObject*>(listBoxOrderList), &e);
264 }
265 
isEditingCurrentOrderlistPattern()266 bool Tracker::isEditingCurrentOrderlistPattern()
267 {
268 	return moduleEditor->isEditingOrderPosition(getOrderListBoxIndex());
269 }
270 
getInstrumentToPlay(pp_int32 note,PlayerController * & playerController)271 pp_int32 Tracker::getInstrumentToPlay(pp_int32 note, PlayerController*& playerController)
272 {
273 	if (PPControl* ctrl = screen->getModalControl())
274 	{
275 		note--;
276 
277 		PPContainer* container = static_cast<PPContainer*>(ctrl);
278 
279 		PPListBox* listBoxSrc = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC));
280 		PPListBox* listBoxSrcSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
281 		PPListBox* listBoxSrcModule = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC3));
282 		PPListBox* listBoxDst = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST));
283 		PPListBox* listBoxDstSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
284 		PPListBox* listBoxDstModule = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST3));
285 
286 		if (!listBoxSrc || !listBoxDst)
287 			return -1;
288 
289 		PPListBox* focusedListBox = static_cast<PPListBox*>(container->getFocusedControl());
290 		if (focusedListBox == NULL)
291 			return getPatternEditorControl()->isInstrumentEnabled() ? listBoxInstruments->getSelectedIndex() + 1 : 0;
292 
293 		// not having any module selection boxes
294 		if (listBoxSrc && listBoxDst &&
295 			!listBoxSrcModule && !listBoxDstModule)
296 		{
297 			// return the selected index from the focused list box
298 			if (focusedListBox == listBoxSrc ||
299 				focusedListBox == listBoxDst)
300 				return focusedListBox->getSelectedIndex() + 1;
301 		}
302 
303 		// focus is on instruments
304 		if (focusedListBox == listBoxSrc ||
305 			focusedListBox == listBoxSrcModule)
306 		{
307 			playerController = tabManager->getPlayerControllerFromTabIndex(listBoxSrcModule->getSelectedIndex());
308 			// return the selected index from the focused list box
309 			return listBoxSrc->getSelectedIndex() + 1;
310 		}
311 		else if (focusedListBox == listBoxDst ||
312 				 focusedListBox == listBoxDstModule)
313 		{
314 			playerController = tabManager->getPlayerControllerFromTabIndex(listBoxDstModule->getSelectedIndex());
315 			// return the selected index from the focused list box
316 			return listBoxDst->getSelectedIndex() + 1;
317 		}
318 
319 		// if focus is on one of the samples list boxes set up some sample playing
320 		// on the sample playing channels of the current player
321 		ModuleEditor* src = listBoxSrcModule ? tabManager->getModuleEditorFromTabIndex(listBoxSrcModule->getSelectedIndex()) : this->moduleEditor;
322 		ModuleEditor* dst = listBoxDstModule ? tabManager->getModuleEditorFromTabIndex(listBoxDstModule->getSelectedIndex()) : this->moduleEditor;
323 
324 		if (focusedListBox == listBoxSrcSmp)
325 		{
326 			SamplePlayer samplePlayer(*src, *playerController);
327 			if (focusedListBox == listBoxSrcSmp)
328 				samplePlayer.playSample(listBoxSrc->getSelectedIndex(),
329 										listBoxSrcSmp->getSelectedIndex(), note);
330 			else
331 				samplePlayer.playSample(listBoxSrc->getSelectedIndex(),
332 										note);
333 			return -1;
334 		}
335 
336 		if (focusedListBox == listBoxDstSmp)
337 		{
338 			SamplePlayer samplePlayer(*dst, *playerController);
339 			if (focusedListBox == listBoxDstSmp)
340 				samplePlayer.playSample(listBoxDst->getSelectedIndex(),
341 										listBoxDstSmp->getSelectedIndex(), note);
342 			else
343 				samplePlayer.playSample(listBoxDst->getSelectedIndex(),
344 										note);
345 			return -1;
346 		}
347 
348 		return focusedListBox->getSelectedIndex() + 1;
349 	}
350 	else
351 	{
352 		return getPatternEditorControl()->isInstrumentEnabled() ? listBoxInstruments->getSelectedIndex() + 1 : 0;
353 	}
354 }
355 
setNumChannels(pp_int32 numChannels,bool repaint)356 void Tracker::setNumChannels(pp_int32 numChannels, bool repaint/* = true*/)
357 {
358 	getPatternEditorControl()->setNumVisibleChannels(numChannels);
359 	scopesControl->setNumChannels(numChannels);
360 	updatePatternEditorControl(repaint, false);
361 }
362 
showSongSettings(bool show)363 void Tracker::showSongSettings(bool show)
364 {
365 	screen->getControlByID(CONTAINER_ABOUT)->show(show);
366 	screen->getControlByID(CONTAINER_ORDERLIST)->show(show);
367 	screen->getControlByID(CONTAINER_SPEED)->show(show);
368 	screen->getControlByID(CONTAINER_PATTERN)->show(show);
369 }
370 
showMainOptions(bool show)371 void Tracker::showMainOptions(bool show)
372 {
373 	screen->getControlByID(CONTAINER_MENU)->show(show);
374 }
375 
showMainMenu(bool show,bool showInstrumentSelector)376 void Tracker::showMainMenu(bool show, bool showInstrumentSelector)
377 {
378 #ifndef __LOWRES__
379 	showSongSettings(show);
380 	showMainOptions(show);
381 	if (showInstrumentSelector)
382 		screen->getControlByID(CONTAINER_INSTRUMENTLIST)->show(show);
383 #else
384 	if (!show)
385 	{
386 		showSongSettings(false);
387 		showMainOptions(false);
388 		screen->getControlByID(CONTAINER_LOWRES_MENUSWITCH)->show(false);
389 		screen->getControlByID(CONTAINER_INSTRUMENTLIST)->show(false);
390 		screen->getControlByID(CONTAINER_LOWRES_TINYMENU)->show(false);
391 	}
392 	else
393 	{
394 		sectionSwitcher->showCurrentSubMenu(false);
395 		screen->getControlByID(CONTAINER_LOWRES_MENUSWITCH)->show(true);
396 	}
397 #endif
398 }
399 
400 #ifdef __LOWRES__
selectScopesControl(pp_int32 ctrlType)401 void Tracker::selectScopesControl(pp_int32 ctrlType)
402 {
403 	scopesControl->setCurrentClickType((ScopesControl::ClickTypes)ctrlType);
404 
405 	updateScopesControlButtons();
406 
407 	screen->paintControl(screen->getControlByID(CONTAINER_SCOPECONTROL));
408 }
409 
updateScopesControlButtons()410 void Tracker::updateScopesControlButtons()
411 {
412 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_SCOPECONTROL));
413 
414 	ASSERT(container);
415 
416 	if (!container->isVisible())
417 		return;
418 
419 	if (!scopesControl)
420 		return;
421 
422 	static_cast<PPButton*>(container->getControlByID(BUTTON_SCOPECONTROL_MUTE))->setPressed(false);
423 	static_cast<PPButton*>(container->getControlByID(BUTTON_SCOPECONTROL_SOLO))->setPressed(false);
424 	static_cast<PPButton*>(container->getControlByID(BUTTON_SCOPECONTROL_REC))->setPressed(false);
425 
426 	switch (scopesControl->getCurrentClickType())
427 	{
428 		case ScopesControl::ClickTypeMute:
429 			static_cast<PPButton*>(container->getControlByID(BUTTON_SCOPECONTROL_MUTE))->setPressed(true);
430 			break;
431 		case ScopesControl::ClickTypeSolo:
432 			static_cast<PPButton*>(container->getControlByID(BUTTON_SCOPECONTROL_SOLO))->setPressed(true);
433 			break;
434 		case ScopesControl::ClickTypeRec:
435 			static_cast<PPButton*>(container->getControlByID(BUTTON_SCOPECONTROL_REC))->setPressed(true);
436 			break;
437 	}
438 }
439 
toggleJamMenuPianoSize()440 void Tracker::toggleJamMenuPianoSize()
441 {
442 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_LOWRES_JAMMENU));
443 	ASSERT(container);
444 
445 	PianoControl* pCtrl = static_cast<PianoControl*>(container->getControlByID(PIANO_CONTROL));
446 	ASSERT(pCtrl);
447 
448 	bool largePiano = (pCtrl->getxScale() == 6 && pCtrl->getyScale() == 3);
449 
450 	PPButton* button = static_cast<PPButton*>(container->getControlByID(BUTTON_JAMMENU_TOGGLEPIANOSIZE));
451 	ASSERT(button);
452 
453 	button->setText(largePiano ? TrackerConfig::stringButtonCollapsed : TrackerConfig::stringButtonExtended);
454 
455 	if (largePiano)
456 	{
457 		container->setSize(PPSize(container->getSize().width, container->getSize().height - 25*2));
458 		container->setLocation(PPPoint(container->getLocation().x, container->getLocation().y + 25*2));
459 		pCtrl->setLocation(PPPoint(pCtrl->getLocation().x, pCtrl->getLocation().y + 25*2));
460 		pCtrl->setxScale(3);
461 		pCtrl->setyScale(1);
462 		pCtrl->setSize(PPSize(screen->getWidth() - 4, 25*1+12));
463 
464 		getPatternEditorControl()->setSize(PPSize(getPatternEditorControl()->getSize().width, getPatternEditorControl()->getSize().height + 25*2));
465 	}
466 	else
467 	{
468 		container->setSize(PPSize(container->getSize().width, container->getSize().height + 25*2));
469 		container->setLocation(PPPoint(container->getLocation().x, container->getLocation().y - 25*2));
470 		pCtrl->setLocation(PPPoint(pCtrl->getLocation().x, pCtrl->getLocation().y - 25*2));
471 		pCtrl->setxScale(6);
472 		pCtrl->setyScale(3);
473 		pCtrl->setSize(PPSize(screen->getWidth() - 4, 25*3+12));
474 
475 		getPatternEditorControl()->setSize(PPSize(getPatternEditorControl()->getSize().width, getPatternEditorControl()->getSize().height - 25*2));
476 	}
477 
478 	screen->paint();
479 }
480 
flipInstrumentListBoxes()481 void Tracker::flipInstrumentListBoxes()
482 {
483 	PPContainer* ctrl = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_INSTRUMENTLIST));
484 
485 	PPListBox* listBoxIns = static_cast<PPListBox*>(ctrl->getControlByID(LISTBOX_INSTRUMENTS));
486 	PPListBox* listBoxSmp = static_cast<PPListBox*>(ctrl->getControlByID(LISTBOX_SAMPLES));
487 
488 	bool b = listBoxIns->isHidden();
489 
490 	PPPoint insPos = listBoxIns->getLocation();
491 	PPSize insSize = listBoxIns->getSize();
492 
493 	PPPoint smpPos = listBoxSmp->getLocation();
494 	PPSize smpSize = listBoxSmp->getSize();
495 
496 	listBoxSmp->setLocation(insPos);
497 	listBoxSmp->setSize(insSize);
498 
499 	listBoxIns->setLocation(smpPos);
500 	listBoxIns->setSize(smpSize);
501 
502 	listBoxSmp->hide(b);
503 	listBoxIns->hide(!b);
504 
505 	ctrl->getControlByID(STATICTEXT_INSTRUMENTS_ALTERNATIVEHEADER)->hide(b);
506 	ctrl->getControlByID(STATICTEXT_INSTRUMENTS_ALTERNATIVEHEADER2)->hide(b);
507 
508 	ctrl->getControlByID(BUTTON_INSTRUMENT)->hide(!b);
509 	ctrl->getControlByID(STATICTEXT_SAMPLEHEADER)->hide(!b);
510 	ctrl->getControlByID(BUTTON_INSTRUMENTS_PLUS)->hide(!b);
511 	ctrl->getControlByID(BUTTON_INSTRUMENTS_MINUS)->hide(!b);
512 
513 	screen->paintControl(ctrl);
514 }
515 
516 #endif
517 
setModuleNumChannels(pp_uint32 numChannels)518 void Tracker::setModuleNumChannels(pp_uint32 numChannels)
519 {
520 	moduleEditor->setNumChannels(numChannels);
521 	setNumChannels(numChannels);
522 }
523 
handleEvent(PPObject * sender,PPEvent * event)524 pp_int32 Tracker::handleEvent(PPObject* sender, PPEvent* event)
525 {
526 	char buffer[100];
527 
528 	if (event->getID() == eFileDragDropped)
529 	{
530 		if (screen->getModalControl())
531 			return 0;
532 		const PPSystemString* str = *(reinterpret_cast<PPSystemString* const*>(event->getDataPtr()));
533 		loadGenericFileType(*str);
534 		event->cancel();
535 	}
536 	else if (event->getID() == eUpdateChanged)
537 	{
538 		updateWindowTitle();
539 	}
540 	else if (event->getID() == eKeyDown ||
541 			 event->getID() == eKeyChar ||
542 			 event->getID() == eKeyUp)
543 	{
544 		processShortcuts(event);
545 	}
546 	else if (event->getID() == eTimer)
547 	{
548 		doFollowSong();
549 	}
550 #ifndef __LOWRES__
551 	else if (event->getID() == eLMouseDown)
552 	{
553 		PPPoint* p = (PPPoint*)event->getDataPtr();
554 		caughtMouseInUpperLeftCorner = (p->x <= TrackerConfig::trackerExitBounds.x && p->y <= TrackerConfig::trackerExitBounds.y) ? true : false;
555 		if (caughtMouseInUpperLeftCorner)
556 			event->cancel();
557 	}
558 	else if (event->getID() == eLMouseUp)
559 	{
560 		PPPoint* p = (PPPoint*)event->getDataPtr();
561 
562 		if ((p->x <= TrackerConfig::trackerExitBounds.x && p->y <= TrackerConfig::trackerExitBounds.y) && caughtMouseInUpperLeftCorner)
563 		{
564 			event->cancel();
565 			eventKeyDownBinding_ExitApplication();
566 		}
567 		else
568 			caughtMouseInUpperLeftCorner = false;
569 	}
570 #endif
571 	else if (event->getID() == eCommand || event->getID() == eCommandRepeat)
572 	{
573 
574 		switch (reinterpret_cast<PPControl*>(sender)->getID())
575 		{
576 			// test
577 			/*case BUTTON_MENU_ITEM_0+8:
578 			{
579 				if (event->getID() != eCommand)
580 					break;
581 
582 				eventKeyDownBinding_InvokeSectionHDRecorder();
583 				break;
584 			}*/
585 
586 			case BUTTON_INSTRUMENT:
587 			{
588 				if (event->getID() != eCommand)
589 					break;
590 
591 				enableInstrument(!getPatternEditorControl()->isInstrumentEnabled());
592 				break;
593 			}
594 
595 			case STATICTEXT_ABOUT_HEADING:
596 			{
597 				if (event->getID() != eCommand)
598 					break;
599 
600 				TitlePageManager titlePageManager(*screen);
601 				titlePageManager.setPeakControlHeadingColor(PPUIConfig::getInstance()->getColor(PPUIConfig::ColorStaticText));
602 				break;
603 			}
604 
605 			case BUTTON_ABOUT_SHOWTITLE:
606 			{
607 				if (event->getID() != eCommand)
608 					break;
609 
610 				TitlePageManager titlePageManager(*screen);
611 				titlePageManager.showTitlePage(TitlePageManager::PageTitle);
612 				break;
613 			}
614 
615 			case BUTTON_ABOUT_SHOWTIME:
616 			{
617 				if (event->getID() != eCommand)
618 					break;
619 				TitlePageManager titlePageManager(*screen);
620 				titlePageManager.showTitlePage(TitlePageManager::PageTime);
621 				break;
622 			}
623 
624 			case BUTTON_ABOUT_ESTIMATESONGLENGTH:
625 			{
626 				if (event->getID() != eCommand)
627 					break;
628 				estimateSongLength(true);
629 				break;
630 			}
631 
632 			case BUTTON_ABOUT_SHOWPEAK:
633 			{
634 				if (event->getID() != eCommand)
635 					break;
636 				TitlePageManager titlePageManager(*screen);
637 				titlePageManager.showTitlePage(TitlePageManager::PagePeak);
638 				break;
639 			}
640 
641 			case BUTTON_ABOUT_FOLLOWSONG:
642 			{
643 				if (event->getID() != eCommand)
644 					break;
645 				eventKeyDownBinding_ToggleFollowSong();
646 				break;
647 			}
648 
649 			case BUTTON_ABOUT_PROSPECTIVE:
650 			{
651 				if (event->getID() != eCommand)
652 					break;
653 				eventKeyDownBinding_ToggleProspectiveMode();
654 				break;
655 			}
656 
657 			case BUTTON_ABOUT_WRAPCURSOR:
658 			{
659 				if (event->getID() != eCommand)
660 					break;
661 				eventKeyDownBinding_ToggleCursorWrapAround();
662 				break;
663 			}
664 
665 			case BUTTON_ABOUT_LIVESWITCH:
666 			{
667 				if (event->getID() != eCommand)
668 					break;
669 				eventKeyDownBinding_ToggleLiveSwitch();
670 				break;
671 			}
672 
673 			case BUTTON_ORDERLIST_EXTENT:
674 			{
675 				if (event->getID() != eCommand)
676 					break;
677 				expandOrderlist(!extendedOrderlist);
678 				screen->paintControl(screen->getControlByID(CONTAINER_ORDERLIST));
679 				break;
680 			}
681 
682 			case BUTTON_SPEEDCONTAINERFLIP:
683 			{
684 				if (event->getID() != eCommand)
685 					break;
686 				flipSpeedSection();
687 				screen->paintControl(screen->getControlByID(CONTAINER_SPEED));
688 				break;
689 			}
690 
691 #ifdef __LOWRES__
692 			// -------- submenus ------------------------------
693 			case BUTTON_APP_EXIT:
694 				if (event->getID() != eCommand)
695 					break;
696 
697 				eventKeyDownBinding_ExitApplication();
698 				break;
699 			case BUTTON_0:
700 			case BUTTON_1:
701 			case BUTTON_2:
702 			case BUTTON_3:
703 			case BUTTON_4:
704 			{
705 				if (event->getID() != eCommand)
706 					break;
707 
708 				PPButton* button = reinterpret_cast<PPButton*>(sender);
709 
710 				sectionSwitcher->switchToSubMenu((SectionSwitcher::ActiveLowerSectionPages)(button->getID() - BUTTON_0));
711 				break;
712 			}
713 #endif
714 
715 			// -------- generic message box -------------------
716 			case PP_MESSAGEBOX_BUTTON_YES:
717 			case PP_MESSAGEBOX_BUTTON_NO:
718 			case PP_MESSAGEBOX_BUTTON_CANCEL:
719 			case PP_MESSAGEBOX_BUTTON_USER1:
720 			case PP_MESSAGEBOX_BUTTON_USER2:
721 			case PP_MESSAGEBOX_BUTTON_USER3:
722 			case PP_MESSAGEBOX_BUTTON_USER4:
723 			case PP_MESSAGEBOX_BUTTON_USER5:
724 			case PP_MESSAGEBOX_BUTTON_USER6:
725 			case PP_MESSAGEBOX_BUTTON_USER7:
726 			case PP_MESSAGEBOX_BUTTON_USER8:
727 			case PP_MESSAGEBOX_BUTTON_USER9:
728 			// little hack, user buttons below PP_MESSAGEBOX_BUTTON_USER10
729 			// are not allowed to send repeatable pressed down events
730 				if (event->getID() != eCommand)
731 					break;
732 			case PP_MESSAGEBOX_BUTTON_USER10:
733 			case PP_MESSAGEBOX_BUTTON_USER11:
734 			case PP_MESSAGEBOX_BUTTON_USER12:
735 			case PP_MESSAGEBOX_BUTTON_USER13:
736 			case PP_MESSAGEBOX_BUTTON_USER14:
737 			case PP_MESSAGEBOX_BUTTON_USER15:
738 			{
739 				bool res = messageBoxEventListener(screen->getModalControl()->getID(),
740 												   reinterpret_cast<PPControl*>(sender)->getID());
741 
742 				if (res)
743 					screen->setModalControl(NULL);  // repaints
744 
745 				break;
746 			}
747 
748 			case MAINMENU_PLAY_SONG:
749 				playerLogic->playSong();
750 				break;
751 
752 			case MAINMENU_PLAY_PATTERN:
753 				playerLogic->playPattern();
754 				break;
755 
756 			case MAINMENU_PLAY_POSITION:
757 				playerLogic->playPosition();
758 				break;
759 
760 			case MAINMENU_STOP:
761 				playerLogic->stopSong();
762 				break;
763 
764 			// -------- ZAP message box ---------------------
765 			case MAINMENU_ZAP:
766 				if (event->getID() != eCommand)
767 					break;
768 
769 				if (dialog)
770 					delete dialog;
771 
772 				if (responder)
773 					delete responder;
774 
775 				responder = new ZapHandler(Zapper(*this));
776 				dialog = new DialogZap(screen, responder, PP_DEFAULT_ID);
777 
778 				dialog->show();
779 				break;
780 
781 			// open song
782 			case MAINMENU_LOAD:
783 			{
784 				if (event->getID() != eCommand)
785 					break;
786 
787 				eventKeyDownBinding_Open();
788 				break;
789 			}
790 
791 			case MAINMENU_SAVE:
792 			{
793 				if (event->getID() != eCommand)
794 					break;
795 
796 				eventKeyDownBinding_Save();
797 				break;
798 			}
799 
800 			case MAINMENU_SAVEAS:
801 			{
802 				if (event->getID() != eCommand)
803 					break;
804 
805 				eventKeyDownBinding_SaveAs();
806 				break;
807 			}
808 
809 			// disk op
810 			case MAINMENU_DISKMENU:
811 			{
812 				if (event->getID() != eCommand)
813 					break;
814 
815 				eventKeyDownBinding_InvokeSectionDiskMenu();
816 				break;
817 			}
818 
819 			// Only Fasttracker II editing mode:
820 			// Edit button
821 			case MAINMENU_EDIT:
822 			{
823 				if (event->getID() != eCommand)
824 					break;
825 
826 				switch (editMode)
827 				{
828 					case EditModeFastTracker:
829 						eventKeyDownBinding_ToggleFT2Edit();
830 						break;
831 
832 					case EditModeMilkyTracker:
833 						eventKeyDownBinding_Edit();
834 						break;
835 				}
836 				break;
837 			}
838 
839 			// instrument editor
840 			case MAINMENU_INSEDIT:
841 			{
842 				if (event->getID() != eCommand)
843 					break;
844 
845 				eventKeyDownBinding_InvokeSectionInstruments();
846 				break;
847 			}
848 
849 			case BUTTON_INSTRUMENTEDITOR_EXIT:
850 			case BUTTON_SAMPLEEDITOR_EXIT:
851 			{
852 				if (event->getID() != eCommand)
853 					break;
854 
855 				sectionSwitcher->showBottomSection(SectionSwitcher::ActiveBottomSectionNone);
856 				screen->paint(true, true);
857 				break;
858 			}
859 
860 			// sample editor
861 			case MAINMENU_SMPEDIT:
862 			{
863 				if (event->getID() != eCommand)
864 					break;
865 
866 				eventKeyDownBinding_InvokeSectionSamples();
867 				break;
868 			}
869 
870 			// settings
871 			case MAINMENU_ADVEDIT:
872 			{
873 				if (event->getID() != eCommand)
874 					break;
875 
876 				eventKeyDownBinding_InvokeSectionAdvancedEdit();
877 				break;
878 			}
879 
880 			// transpose
881 			case MAINMENU_TRANSPOSE:
882 			{
883 				if (event->getID() != eCommand)
884 					break;
885 
886 				eventKeyDownBinding_InvokeSectionTranspose();
887 				break;
888 			}
889 
890 			// settings
891 			case MAINMENU_CONFIG:
892 			{
893 				if (event->getID() != eCommand)
894 					break;
895 
896 				eventKeyDownBinding_InvokeSectionSettings();
897 				break;
898 			}
899 
900 			// quick options
901 			case MAINMENU_QUICKOPTIONS:
902 			{
903 				if (event->getID() != eCommand)
904 					break;
905 
906 				eventKeyDownBinding_InvokeSectionQuickOptions();
907 				break;
908 			}
909 
910 			// optimize
911 			case MAINMENU_OPTIMIZE:
912 			{
913 				if (event->getID() != eCommand)
914 					break;
915 
916 				eventKeyDownBinding_InvokeSectionOptimize();
917 				break;
918 			}
919 
920 			case MAINMENU_ABOUT:
921 			{
922 				if (event->getID() != eCommand)
923 					break;
924 
925 				eventKeyDownBinding_InvokeSectionAbout();
926 				break;
927 			}
928 
929 #ifdef __LOWRES__
930 			case BUTTON_SAMPLES_INVOKEHDRECORDER:
931 			{
932 				if (event->getID() != eCommand)
933 					break;
934 
935 				// The bottom section fills up the entire screen
936 				// so we first need to hide the entire section before we can show
937 				// the HD recorder section
938 				screen->pauseUpdate(true);
939 				sectionSwitcher->hideBottomSection();
940 
941 				sectionHDRecorder->selectSampleOutput();
942 				eventKeyDownBinding_InvokeSectionHDRecorder();
943 				screen->pauseUpdate(false);
944 				screen->paint();
945 				break;
946 			}
947 #endif
948 
949 			case BUTTON_ORDERLIST_SONGLENGTH_PLUS:
950 				moduleEditor->increaseSongLength();
951 				updateSongLength();
952 				sectionHDRecorder->adjustOrders();
953 				break;
954 
955 			case BUTTON_ORDERLIST_SONGLENGTH_MINUS:
956 				moduleEditor->decreaseSongLength();
957 				updateSongLength();
958 				sectionHDRecorder->adjustOrders();
959 				break;
960 
961 			case BUTTON_ORDERLIST_REPEAT_PLUS:
962 				moduleEditor->increaseRepeatPos();
963 				updateSongRepeat();
964 				break;
965 
966 			case BUTTON_ORDERLIST_REPEAT_MINUS:
967 				moduleEditor->decreaseRepeatPos();
968 				updateSongRepeat();
969 				break;
970 
971 			// insert position into orderlist
972 			case BUTTON_ORDERLIST_INSERT:
973 				moduleEditor->insertNewOrderPosition(getOrderListBoxIndex());
974 				updateOrderlist();
975 				sectionHDRecorder->adjustOrders();
976 				playerLogic->continuePlayingSong();
977 				break;
978 
979 			// delete current orderlist position
980 			case BUTTON_ORDERLIST_DELETE:
981 				moduleEditor->deleteOrderPosition(getOrderListBoxIndex());
982 				updateOrderlist();
983 				sectionHDRecorder->adjustOrders();
984 				playerLogic->continuePlayingSong();
985 				break;
986 
987 			// insert position into orderlist
988 			case BUTTON_ORDERLIST_SEQENTRY:
989 			{
990 				moduleEditor->seqCurrentOrderPosition(getOrderListBoxIndex());
991 				updateSongLength(false);
992 				pp_int32 index = getOrderListBoxIndex()+1;
993 				setOrderListIndex(index);
994 				sectionHDRecorder->adjustOrders();
995 				playerLogic->continuePlayingSong();
996 				break;
997 			}
998 
999 			// insert position into orderlist and clone the current selected pattern
1000 			case BUTTON_ORDERLIST_CLNENTRY:
1001 			{
1002 				moduleEditor->seqCurrentOrderPosition(getOrderListBoxIndex(), true);
1003 				updateSongLength(false);
1004 				pp_int32 index = getOrderListBoxIndex()+1;
1005 				setOrderListIndex(index);
1006 				sectionHDRecorder->adjustOrders();
1007 				playerLogic->continuePlayingSong();
1008 				break;
1009 			}
1010 
1011 			// select next pattern in current orderlist position
1012 			case BUTTON_ORDERLIST_NEXT:
1013 				moduleEditor->increaseOrderPosition(getOrderListBoxIndex());
1014 				updateOrderlist();
1015 				playerLogic->continuePlayingSong();
1016 				break;
1017 
1018 			// select previous pattern in current orderlist position
1019 			case BUTTON_ORDERLIST_PREVIOUS:
1020 				moduleEditor->decreaseOrderPosition(getOrderListBoxIndex());
1021 				updateOrderlist();
1022 				playerLogic->continuePlayingSong();
1023 				break;
1024 
1025 			case BUTTON_OCTAVE_MINUS:
1026 				getPatternEditor()->decreaseCurrentOctave();
1027 				updatePatternAddAndOctave();
1028 				break;
1029 			case BUTTON_OCTAVE_PLUS:
1030 				getPatternEditor()->increaseCurrentOctave();
1031 				updatePatternAddAndOctave();
1032 				break;
1033 
1034 			case BUTTON_ADD_PLUS:
1035 				getPatternEditorControl()->increaseRowInsertAdd();
1036 				updatePatternAddAndOctave();
1037 				break;
1038 
1039 			case BUTTON_ADD_MINUS:
1040 				getPatternEditorControl()->decreaseRowInsertAdd();
1041 				updatePatternAddAndOctave();
1042 				break;
1043 
1044 			case BUTTON_BPM_PLUS:
1045 			{
1046 				setChanged();
1047 				mp_sint32 bpm,speed;
1048 				playerController->getSpeed(bpm, speed);
1049 				playerController->setSpeed(bpm+1, speed);
1050 				updateSpeed();
1051 				break;
1052 			}
1053 
1054 			case BUTTON_BPM_MINUS:
1055 			{
1056 				setChanged();
1057 				mp_sint32 bpm,speed;
1058 				playerController->getSpeed(bpm, speed);
1059 				playerController->setSpeed(bpm-1, speed);
1060 				updateSpeed();
1061 				break;
1062 			}
1063 
1064 			case BUTTON_SPEED_PLUS:
1065 			{
1066 				setChanged();
1067 				mp_sint32 bpm,speed;
1068 				playerController->getSpeed(bpm, speed);
1069 				playerController->setSpeed(bpm, speed+1);
1070 				updateSpeed();
1071 				break;
1072 			}
1073 
1074 			case BUTTON_SPEED_MINUS:
1075 			{
1076 				setChanged();
1077 				mp_sint32 bpm,speed;
1078 				playerController->getSpeed(bpm, speed);
1079 				playerController->setSpeed(bpm, speed-1);
1080 				updateSpeed();
1081 				break;
1082 			}
1083 
1084 			// go to next pattern
1085 			case BUTTON_PATTERN_PLUS:
1086 				eventKeyDownBinding_NextPattern();
1087 				break;
1088 
1089 			// go to previous pattern
1090 			case BUTTON_PATTERN_MINUS:
1091 				eventKeyDownBinding_PreviousPattern();
1092 				break;
1093 
1094 			// expand current pattern
1095 			case BUTTON_PATTERN_EXPAND:
1096 				getPatternEditor()->expandPattern();
1097 				updatePatternLength(false);
1098 				screen->update();
1099 				break;
1100 
1101 			// shrink current pattern
1102 			case BUTTON_PATTERN_SHRINK:
1103 				getPatternEditor()->shrinkPattern();
1104 				updatePatternLength(false);
1105 				screen->update();
1106 				break;
1107 
1108 			// grow length
1109 			case BUTTON_PATTERN_SIZE_PLUS:
1110 				getPatternEditor()->resizePattern(moduleEditor->getPattern(moduleEditor->getCurrentPatternIndex())->rows + 1);
1111 				updatePatternLength(false);
1112 				screen->update();
1113 				break;
1114 
1115 			// decrease length
1116 			case BUTTON_PATTERN_SIZE_MINUS:
1117 				getPatternEditor()->resizePattern(moduleEditor->getPattern(moduleEditor->getCurrentPatternIndex())->rows - 1);
1118 				updatePatternLength(false);
1119 				screen->update();
1120 				break;
1121 
1122 #ifdef __LOWRES__
1123 			// go to next order
1124 			case BUTTON_JAMMENU_NEXTORDERLIST:
1125 				selectNextOrder();
1126 				break;
1127 
1128 			// go to previous order
1129 			case BUTTON_JAMMENU_PREVORDERLIST:
1130 				selectPreviousOrder();
1131 				break;
1132 
1133 			case BUTTON_JAMMENU_NEXTINSTRUMENT:
1134 				selectNextInstrument();
1135 				break;
1136 
1137 			case BUTTON_JAMMENU_PREVINSTRUMENT:
1138 				selectPreviousInstrument();
1139 				break;
1140 
1141 			case BUTTON_JAMMENU_TOGGLEPIANOSIZE:
1142 				if (event->getID() != eCommand)
1143 					break;
1144 
1145 				toggleJamMenuPianoSize();
1146 				break;
1147 
1148 			case BUTTON_INSTRUMENTS_FLIP:
1149 				if (event->getID() != eCommand)
1150 					break;
1151 
1152 				flipInstrumentListBoxes();
1153 				break;
1154 #endif
1155 
1156 			// add channels to song (affects pattern editor)
1157 			case BUTTON_MENU_ITEM_ADDCHANNELS:
1158 			case BUTTON_MENU_ITEM_SUBCHANNELS:
1159 			{
1160 				mp_sint32 numChannels = moduleEditor->getNumChannels() +
1161 										(reinterpret_cast<PPControl*>(sender)->getID() == BUTTON_MENU_ITEM_ADDCHANNELS ? 2 : -2);
1162 
1163 				if (numChannels > TrackerConfig::numPlayerChannels)
1164 					numChannels = TrackerConfig::numPlayerChannels;
1165 				if (numChannels < 2)
1166 					numChannels = 2;
1167 
1168 				setModuleNumChannels(numChannels);
1169 				break;
1170 			}
1171 
1172 			case BUTTON_INSTRUMENTS_PLUS:
1173 				moduleEditor->allocateInstrument();
1174 				updateInstrumentsListBox(false);
1175 				sectionInstruments->update(false);
1176 				screen->update();
1177 				break;
1178 
1179 			case BUTTON_INSTRUMENTS_MINUS:
1180 			{
1181 				pp_uint32 i = listBoxInstruments->getSelectedIndex();
1182 				moduleEditor->freeInstrument();
1183 				updateInstrumentsListBox(false);
1184 
1185 				if (listBoxInstruments->getSelectedIndex() != i)
1186 				{
1187 					getPatternEditorControl()->setCurrentInstrument(listBoxInstruments->getSelectedIndex() + 1);
1188 					updateSampleEditorAndInstrumentSection(false);
1189 				}
1190 
1191 				screen->update();
1192 				break;
1193 			}
1194 
1195 			case BUTTON_TAB_OPEN:
1196 			{
1197 				eventKeyDownBinding_OpenTab();
1198 				break;
1199 			}
1200 
1201 			case BUTTON_TAB_CLOSE:
1202 			{
1203 				eventKeyDownBinding_CloseTab();
1204 				break;
1205 			}
1206 
1207 #ifdef __LOWRES__
1208 			case BUTTON_SCOPECONTROL_MUTE:
1209 				if (event->getID() != eCommand)
1210 					break;
1211 				selectScopesControl(ScopesControl::ClickTypeMute);
1212 				break;
1213 
1214 			case BUTTON_SCOPECONTROL_SOLO:
1215 				if (event->getID() != eCommand)
1216 					break;
1217 				selectScopesControl(ScopesControl::ClickTypeSolo);
1218 				break;
1219 
1220 			case BUTTON_SCOPECONTROL_REC:
1221 				if (event->getID() != eCommand)
1222 					break;
1223 				selectScopesControl(ScopesControl::ClickTypeRec);
1224 				break;
1225 #endif
1226 		}
1227 
1228 		// Check if something has changed
1229 		updateWindowTitle();
1230 
1231 	}
1232 	else if (event->getID() == ePreSelection)
1233 	{
1234 		switch (reinterpret_cast<PPControl*>(sender)->getID())
1235 		{
1236 			// new instrument has been selected, we need to assure
1237 			// that the sample changes are committed before we wipe out
1238 			// the current sample listbox data
1239 			case LISTBOX_INSTRUMENTS:
1240 				if (listBoxSamples->isEditing())
1241 					listBoxSamples->commitChanges();
1242 				break;
1243 		}
1244 	}
1245 	else if (event->getID() == eSelection)
1246 	{
1247 		switch (reinterpret_cast<PPControl*>(sender)->getID())
1248 		{
1249 			case TABHEADER_CONTROL:
1250 			{
1251 				pp_int32 index = *((pp_int32*)event->getDataPtr());
1252 				tabManager->switchToTab(index);
1253 				break;
1254 			}
1255 
1256 			// new pattern has been selected in the orderlist
1257 			case LISTBOX_ORDERLIST:
1258 			{
1259 				pp_int32 orderIndex = *((pp_int32*)event->getDataPtr());
1260 				moduleEditor->setCurrentOrderIndex(orderIndex);
1261 				moduleEditor->setCurrentPatternIndex(moduleEditor->getOrderPosition(orderIndex));
1262 
1263 				updatePatternEditorControl(false);
1264 				updatePatternIndex(false);
1265 				updatePatternLength(false);
1266 #ifdef __LOWRES__
1267 				updateJamMenuOrder(false);
1268 #endif
1269 				screen->update();
1270 
1271 				if (playerController->isPlayingPattern())
1272 					playerLogic->continuePlayingPattern();
1273 				else
1274 					playerLogic->continuePlayingSong();
1275 				break;
1276 			}
1277 
1278 			// new instrument has been selected
1279 			case LISTBOX_INSTRUMENTS:
1280 			{
1281 				pp_int32 index = *((pp_int32*)event->getDataPtr()) + 1;
1282 				selectInstrument(index);
1283 				screen->update();
1284 				break;
1285 			}
1286 
1287 			case LISTBOX_SAMPLES:
1288 			{
1289 				pp_int32 index = *((pp_int32*)event->getDataPtr());
1290 				moduleEditor->setCurrentSampleIndex(index);
1291 				updateSampleEditorAndInstrumentSection(false);
1292 				for (pp_int32 i = 0; i < sections->size(); i++)
1293 					sections->get(i)->notifySampleSelect(index);
1294 				screen->update();
1295 				break;
1296 			}
1297 
1298 			// Instrument chooser
1299 			case INSTRUMENT_CHOOSER_LIST_SRC3:
1300 			case INSTRUMENT_CHOOSER_LIST_DST3:
1301 			{
1302 				PPContainer* container = static_cast<PPContainer*>(screen->getModalControl());
1303 
1304 				PPStaticText* staticText = static_cast<PPStaticText*>(container->getControlByID(INSTRUMENT_CHOOSER_USERSTR1));
1305 
1306 				PPListBox* listBoxChangeIns = NULL;
1307 				PPListBox* listBoxChangeSmp = NULL;
1308 
1309 				PPListBox* listBoxSrcIns = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC));
1310 				PPListBox* listBoxSrcSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
1311 
1312 				PPListBox* listBoxDstIns = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST));;
1313 				PPListBox* listBoxDstSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
1314 
1315 				if (reinterpret_cast<PPControl*>(sender)->getID() == INSTRUMENT_CHOOSER_LIST_SRC3)
1316 				{
1317 					listBoxChangeIns = listBoxSrcIns;
1318 					listBoxChangeSmp = listBoxSrcSmp;
1319 				}
1320 				else if (reinterpret_cast<PPControl*>(sender)->getID() == INSTRUMENT_CHOOSER_LIST_DST3)
1321 				{
1322 					listBoxChangeIns = listBoxDstIns;
1323 					listBoxChangeSmp = listBoxDstSmp;
1324 				}
1325 
1326 				if (listBoxChangeIns && listBoxChangeSmp)
1327 				{
1328 					listBoxChangeIns->clear();
1329 					listBoxChangeSmp->clear();
1330 
1331 					fillInstrumentListBox(listBoxChangeIns,
1332 										  tabManager->getModuleEditorFromTabIndex((reinterpret_cast<PPListBox*>(sender))->getSelectedIndex()));
1333 
1334 					fillSampleListBox(listBoxChangeSmp, listBoxChangeIns->getSelectedIndex(),
1335 									  tabManager->getModuleEditorFromTabIndex((reinterpret_cast<PPListBox*>(sender))->getSelectedIndex()));
1336 				}
1337 
1338 				// update text depending on the type of the instrument chooser dialog
1339 				switch (container->getID())
1340 				{
1341 					case INSTRUMENT_CHOOSER_COPY:
1342 						sprintf(buffer, "Copy ins. %x to %x", listBoxSrcIns->getSelectedIndex()+1, listBoxDstIns->getSelectedIndex()+1);
1343 						break;
1344 					case INSTRUMENT_CHOOSER_SWAP:
1345 						sprintf(buffer, "Swap ins. %x with %x", listBoxSrcSmp->getSelectedIndex()+1, listBoxDstSmp->getSelectedIndex()+1);
1346 						break;
1347 				}
1348 
1349 				staticText->setText(buffer);
1350 				screen->paintControl(screen->getModalControl());
1351 				break;
1352 			}
1353 
1354 			// Instrument chooser
1355 			case INSTRUMENT_CHOOSER_LIST_SRC:
1356 			case INSTRUMENT_CHOOSER_LIST_DST:
1357 			{
1358 				PPContainer* container = static_cast<PPContainer*>(screen->getModalControl());
1359 
1360 				PPStaticText* staticText = static_cast<PPStaticText*>(container->getControlByID(INSTRUMENT_CHOOSER_USERSTR1));
1361 
1362 				PPListBox* listBoxSrc = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC));
1363 				PPListBox* listBoxDst = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST));
1364 
1365 				PPListBox* listBoxSrcModule = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC3));
1366 				PPListBox* listBoxDstModule = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST3));
1367 
1368 				ModuleEditor* src = listBoxSrcModule ? tabManager->getModuleEditorFromTabIndex(listBoxSrcModule->getSelectedIndex()) : this->moduleEditor;
1369 				ModuleEditor* dst = listBoxDstModule ? tabManager->getModuleEditorFromTabIndex(listBoxDstModule->getSelectedIndex()) : this->moduleEditor;
1370 
1371 				PPListBox* listBoxChange = NULL;
1372 				ModuleEditor* moduleEditor = this->moduleEditor;
1373 
1374 				// A new instrument has been selected in either of the two instrument list boxes
1375 				// now it's up to update the samples belonging to the instrument in the sample listboxes
1376 				if (reinterpret_cast<PPControl*>(sender)->getID() == INSTRUMENT_CHOOSER_LIST_SRC)
1377 				{
1378 					listBoxChange = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
1379 					moduleEditor = src;
1380 				}
1381 				else if (reinterpret_cast<PPControl*>(sender)->getID() == INSTRUMENT_CHOOSER_LIST_DST)
1382 				{
1383 					listBoxChange = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
1384 					moduleEditor = dst;
1385 				}
1386 
1387 				if (listBoxChange)
1388 				{
1389 					listBoxChange->clear();
1390 					fillSampleListBox(listBoxChange, reinterpret_cast<PPListBox*>(sender)->getSelectedIndex(), moduleEditor);
1391 				}
1392 
1393 				// update text depending on the type of the instrument chooser dialog
1394 				switch (container->getID())
1395 				{
1396 					case INSTRUMENT_CHOOSER_COPY:
1397 						sprintf(buffer, "Copy ins. %x to %x", listBoxSrc->getSelectedIndex()+1, listBoxDst->getSelectedIndex()+1);
1398 						break;
1399 					case INSTRUMENT_CHOOSER_SWAP:
1400 						sprintf(buffer, "Swap ins. %x with %x", listBoxSrc->getSelectedIndex()+1, listBoxDst->getSelectedIndex()+1);
1401 						break;
1402 					case MESSAGEBOX_INSREMAP:
1403 						sprintf(buffer, "Remap ins. %x to %x", listBoxSrc->getSelectedIndex()+1, listBoxDst->getSelectedIndex()+1);
1404 						break;
1405 				}
1406 
1407 				staticText->setText(buffer);
1408 				screen->paintControl(screen->getModalControl());
1409 				break;
1410 			}
1411 
1412 			case INSTRUMENT_CHOOSER_LIST_SRC2:
1413 			case INSTRUMENT_CHOOSER_LIST_DST2:
1414 			{
1415 				PPContainer* container = static_cast<PPContainer*>(screen->getModalControl());
1416 
1417 				PPStaticText* staticText = static_cast<PPStaticText*>(container->getControlByID(INSTRUMENT_CHOOSER_USERSTR2));
1418 
1419 				PPListBox* listBoxSrc = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
1420 				PPListBox* listBoxDst = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
1421 
1422 				switch (container->getID())
1423 				{
1424 					case INSTRUMENT_CHOOSER_COPY:
1425 						sprintf(buffer, "Copy smp. %x to %x", listBoxSrc->getSelectedIndex(), listBoxDst->getSelectedIndex());
1426 						break;
1427 					case INSTRUMENT_CHOOSER_SWAP:
1428 						sprintf(buffer, "Swap smp. %x with %x", listBoxSrc->getSelectedIndex(), listBoxDst->getSelectedIndex());
1429 						break;
1430 				}
1431 
1432 				staticText->setText(buffer);
1433 				screen->paintControl(screen->getModalControl());
1434 				break;
1435 			}
1436 
1437 		}
1438 
1439 	}
1440 	else if (event->getID() == eValueChanged)
1441 	{
1442 		switch (reinterpret_cast<PPControl*>(sender)->getID())
1443 		{
1444 			case LISTBOX_SONGTITLE:
1445 			{
1446 				moduleEditor->setTitle(**(reinterpret_cast<const PPString* const*>(event->getDataPtr())), ModuleEditor::MAX_TITLETEXT);
1447 				break;
1448 			}
1449 
1450 			case LISTBOX_INSTRUMENTS:
1451 			{
1452 				moduleEditor->setInstrumentName(listBoxInstruments->getSelectedIndex(),
1453 												**(reinterpret_cast<const PPString* const*>(event->getDataPtr())), ModuleEditor::MAX_INSTEXT);
1454 				break;
1455 			}
1456 
1457 			case LISTBOX_SAMPLES:
1458 			{
1459 				moduleEditor->setCurrentSampleName(**(reinterpret_cast<const PPString* const*>(event->getDataPtr())),
1460 												   ModuleEditor::MAX_SMPTEXT);
1461 				break;
1462 			}
1463 
1464 			// channels have been muted/unmuted in pattern editor
1465 			case PATTERN_EDITOR:
1466 			{
1467 				const bool* muteChannelsPtr = reinterpret_cast<const bool*>(event->getDataPtr());
1468 
1469 				for (pp_int32 i = 0; i < TrackerConfig::numPlayerChannels; i++)
1470 				{
1471 					muteChannels[i] = muteChannelsPtr[i];
1472 					bool b = (muteChannels[i] != 0);
1473 					playerController->muteChannel(i, b);
1474 					scopesControl->muteChannel(i, b);
1475 				}
1476 
1477 				if (scopesControl->needsUpdate())
1478 					screen->paintControl(scopesControl);
1479 				break;
1480 			}
1481 
1482 			// channels have been muted/unmuted in scopes
1483 			case SCOPES_CONTROL:
1484 			{
1485 				switch (event->getMetaData())
1486 				{
1487 					case ScopesControl::ChangeValueMuting:
1488 					{
1489 						const bool* muteChannelsPtr = reinterpret_cast<const bool*>(event->getDataPtr());
1490 
1491 						for (pp_int32 i = 0; i < TrackerConfig::numPlayerChannels; i++)
1492 						{
1493 							muteChannels[i] = muteChannelsPtr[i];
1494 							bool b = (muteChannels[i] != 0);
1495 							playerController->muteChannel(i, b);
1496 							getPatternEditorControl()->muteChannel(i, b);
1497 						}
1498 
1499 						if (scopesControl->needsUpdate())
1500 							screen->paintControl(scopesControl, false);
1501 						screen->paintControl(getPatternEditorControl(), false);
1502 
1503 						screen->update();
1504 						break;
1505 					}
1506 
1507 					case ScopesControl::ChangeValueRecording:
1508 					{
1509 						const pp_uint8* recordChannelsPtr = reinterpret_cast<const pp_uint8*>(event->getDataPtr());
1510 
1511 						for (pp_int32 i = 0; i < TrackerConfig::numPlayerChannels; i++)
1512 						{
1513 							bool b = (recordChannelsPtr[i] != 0);
1514 							playerController->recordChannel(i, b);
1515 							getPatternEditorControl()->recordChannel(i, b);
1516 						}
1517 
1518 						playerController->resetFirstPlayingChannel();
1519 
1520 						if (scopesControl->needsUpdate())
1521 							screen->paintControl(scopesControl, false);
1522 
1523 						screen->update();
1524 						break;
1525 					}
1526 				}
1527 				break;
1528 			}
1529 
1530 		}
1531 
1532 		// Check if something has changed
1533 		updateWindowTitle();
1534 
1535 	}
1536 	else if (event->getID() == eUpdated)
1537 	{
1538 		PatternEditorControl* patternEditorControl = reinterpret_cast<PatternEditorControl*>(sender);
1539 		PatternEditor* patternEditor = patternEditorControl->getPatternEditor();
1540 		pp_int32 numRows = patternEditor->getNumRows();
1541 		pp_int32 row = patternEditorControl->getCurrentRow();
1542 
1543 		bool isPlaying = playerController->isPlaying() && !playerController->isPlayingRowOnly();
1544 
1545 		switch (reinterpret_cast<PPControl*>(sender)->getID())
1546 		{
1547 			// The pattern editor sends PPEvent::eUpated when the cursor has been moved
1548 			// It can either be moved out of the current pattern or within the pattern
1549 			// Depending on what the user data says
1550 			case PATTERN_EDITOR:
1551 			{
1552 				switch (*(pp_int32*)event->getDataPtr())
1553 				{
1554 					case PatternEditorControl::AdvanceCodeJustUpdate:
1555 						updatePatternLength();
1556 						updatePatternAddAndOctave();
1557 						break;
1558 
1559 					// End of pattern has been wrapped with cursor down
1560 					case PatternEditorControl::AdvanceCodeCursorDownWrappedEnd:
1561 					{
1562 						bool b = isEditingCurrentOrderlistPattern() && listBoxOrderList->isLastEntry();
1563 						// When we're playing and we're not playing a pattern
1564 						// OR when we're not playing and NOT wrapping cursor around
1565 						// => select next order from orderlist
1566 						if ((isPlaying && !playerController->isReallyPlayingPattern()) ||
1567 							(playerController->isPlaying() && playerController->isPlayingPattern() && playerLogic->rowPlay) ||
1568 							(!isPlaying && !patternEditorControl->getWrapAround()))
1569 						{
1570 							// we're editing the pattern from the current selected order and this is not the last order list entry
1571 							// advance to the next order in the list
1572 							if (!b)
1573 							{
1574 								screen->pauseUpdate(true);
1575 								selectNextOrder();
1576 								screen->pauseUpdate(false);
1577 							}
1578 						}
1579 						// Calculate new position within pattern and update player position
1580 						ASSERT(row > 0);
1581 
1582 						// when we're in wraparound mode and we're not in the last
1583 						// pattern & row of the last order, wrap around cursor in current pattern
1584 						if (!b || patternEditorControl->getWrapAround())
1585 							patternEditorControl->setRow(myMod(row - numRows, patternEditor->getNumRows()), false);
1586 						else
1587 							patternEditorControl->setRow(numRows-1, false);
1588 
1589 						updateSongLength(true);
1590 						updatePatternIndex(true);
1591 						updatePatternLength(true);
1592 						updateSongRow();
1593 						return 1;
1594 					}
1595 
1596 					// Start of pattern has been wrapped with cursor up
1597 					case PatternEditorControl::AdvanceCodeCursorUpWrappedStart:
1598 					{
1599 						bool b = isEditingCurrentOrderlistPattern() && listBoxOrderList->isFirstEntry();
1600 						// When we're playing and we're not playing a pattern
1601 						// OR when we're not playing and NOT wrapping cursor around
1602 						// => select previous order from orderlist
1603 						if ((isPlaying && !playerController->isReallyPlayingPattern()) ||
1604 							(playerController->isPlaying() && playerController->isPlayingPattern() && playerLogic->rowPlay) ||
1605 							(!isPlaying && !patternEditorControl->getWrapAround()))
1606 						{
1607 							// we're editing the pattern from the current selected order and this is not the last order list entry
1608 							// advance to the previous order in the list
1609 							if (!b)
1610 							{
1611 								screen->pauseUpdate(true);
1612 								selectPreviousOrder();
1613 								screen->pauseUpdate(false);
1614 							}
1615 						}
1616 						// Calculate new position within pattern and update player position
1617 						ASSERT(row < 0);
1618 
1619 						// when we're in wraparound mode and we're not in the last
1620 						// pattern & row of the last order, wrap around cursor in current pattern
1621 						if (!b || patternEditorControl->getWrapAround())
1622 							patternEditorControl->setRow(myMod(patternEditor->getNumRows() + row, patternEditor->getNumRows()), false);
1623 						else
1624 							patternEditorControl->setRow(0, false);
1625 
1626 						updateSongLength(true);
1627 						updatePatternIndex(true);
1628 						updatePatternLength(true);
1629 						updateSongRow();
1630 						return 1;
1631 					}
1632 
1633 					// End of pattern has been wrapped with page down key
1634 					case PatternEditorControl::AdvanceCodeCursorPageDownWrappedEnd:
1635 						// Not playing? Do nothing at all
1636 						if (!playerController->isPlaying()/* && !patternEditor->getWrapAround()*/)
1637 							return 0;
1638 						// Last entry in orderlist? Do nothing at all
1639 						//if (listBoxOrderList->isLastEntry())
1640 						//	return 0;
1641 
1642 						// Not playing pattern? Select new order
1643 						if (!playerController->isReallyPlayingPattern())
1644 						{
1645 							screen->pauseUpdate(true);
1646 							selectNextOrder(true);
1647 							screen->pauseUpdate(false);
1648 						}
1649 
1650 						// Calculate new position within pattern and update player position
1651 						ASSERT(row > 0);
1652 						patternEditorControl->setRow(myMod(row - numRows, patternEditor->getNumRows()), false);
1653 						updateSongLength(true);
1654 						updatePatternIndex(true);
1655 						updatePatternLength(true);
1656 						updateSongRow();
1657 						return 1;
1658 
1659 					// Start of pattern has been wrapped with page up key
1660 					case PatternEditorControl::AdvanceCodeCursorPageUpWrappedStart:
1661 						// Not playing? Do nothing at all
1662 						if (!playerController->isPlaying()/* && !patternEditor->getWrapAround()*/)
1663 							return 0;
1664 						// Last entry in orderlist? Do nothing at all
1665 						//if (listBoxOrderList->isFirstEntry())
1666 						//	return 0;
1667 
1668 						// Not playing pattern? Select previous order
1669 						if (!playerController->isReallyPlayingPattern())
1670 						{
1671 							screen->pauseUpdate(true);
1672 							selectPreviousOrder(true);
1673 							screen->pauseUpdate(false);
1674 						}
1675 
1676 						// Calculate new position within pattern and update player position
1677 						ASSERT(row < 0);
1678 						patternEditorControl->setRow(myMod(patternEditor->getNumRows() + row, patternEditor->getNumRows()), false);
1679 						updateSongLength(true);
1680 						updatePatternIndex(true);
1681 						updatePatternLength(true);
1682 						updateSongRow();
1683 						return 1;
1684 
1685 					// End of pattern has been wrapped with page down key
1686 					case PatternEditorControl::AdvanceCodeWrappedEnd:
1687 						if (!patternEditorControl->getWrapAround() && !listBoxOrderList->isLastEntry())
1688 						{
1689 							screen->pauseUpdate(true);
1690 							selectNextOrder(true);
1691 							screen->pauseUpdate(false);
1692 							patternEditorControl->setRow(myMod(row - numRows, patternEditor->getNumRows()), false);
1693 						}
1694 						// if we are in the last order of the order list and in the last row of this order don't advance to the next pattern
1695 						else if (!patternEditorControl->getWrapAround() && listBoxOrderList->isLastEntry())
1696 						{
1697 							patternEditorControl->setRow(numRows - 1, false);
1698 						}
1699 						else
1700 						{
1701 							// Calculate new position within pattern and update player position
1702 							ASSERT(row > 0);
1703 							patternEditorControl->setRow(myMod(row - numRows, patternEditor->getNumRows()), false);
1704 						}
1705 
1706 						updateSongLength(true);
1707 						updatePatternIndex(true);
1708 						updatePatternLength(true);
1709 						updateSongRow();
1710 						return 1;
1711 
1712 					// Not wrapped at all... Just calculate new position within pattern and update player position
1713 					case PatternEditorControl::AdvanceCodeSelectNewRow:
1714 						if (shouldFollowSong() &&
1715 							isEditingCurrentOrderlistPattern())
1716 							updateSongRow();
1717 						break;
1718 				}
1719 				break;
1720 			}
1721 		}
1722 	}
1723 	// frontend layer sends PPEvent::eFullScreen when the APP is going fullscreen
1724 	else if (event->getID() == eFullScreen)
1725 	{
1726 		TrackerSettingsDatabase* settingsDatabaseCopySecond = new TrackerSettingsDatabase(*settingsDatabase);
1727 
1728 		if (settingsDatabaseCopy)
1729 			settingsDatabaseCopy->store("FULLSCREEN", !screen->isFullScreen());
1730 		settingsDatabaseCopySecond->store("FULLSCREEN", !screen->isFullScreen());
1731 
1732 		applySettings(settingsDatabaseCopySecond, settingsDatabase);
1733 
1734 		delete settingsDatabase;
1735 		settingsDatabase = settingsDatabaseCopySecond;
1736 
1737 		sectionSettings->update();
1738 	}
1739 
1740 	return 0;
1741 }
1742 
1743 ///////////////////////////////////////////////////////////////////////////////
1744 // remember:
1745 // user buttons below ID PP_MESSAGEBOX_BUTTON_USER10
1746 // are not allowed to send repeatable pressed down events
1747 ///////////////////////////////////////////////////////////////////////////////
swapAndCopyHandler(pp_int32 messageBoxID,pp_int32 messageBoxButtonID)1748 bool Tracker::swapAndCopyHandler(pp_int32 messageBoxID, pp_int32 messageBoxButtonID)
1749 {
1750 	PPContainer* container = static_cast<PPContainer*>(screen->getModalControl());
1751 	PPListBox* listBoxSrc = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC));
1752 	PPListBox* listBoxSrcModule = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC3));
1753 	PPListBox* listBoxDst = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST));
1754 	PPListBox* listBoxDstModule = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST3));
1755 
1756 	ModuleEditor* src = listBoxSrcModule ? tabManager->getModuleEditorFromTabIndex(listBoxSrcModule->getSelectedIndex()) : this->moduleEditor;
1757 	ModuleEditor* dst = listBoxDstModule ? tabManager->getModuleEditorFromTabIndex(listBoxDstModule->getSelectedIndex()) : this->moduleEditor;
1758 
1759 	switch (messageBoxButtonID)
1760 	{
1761 		case PP_MESSAGEBOX_BUTTON_USER1:
1762 		case PP_MESSAGEBOX_BUTTON_USER3:
1763 		{
1764 			switch (messageBoxID)
1765 			{
1766 				case INSTRUMENT_CHOOSER_COPY:
1767 					this->moduleEditor->copyInstrument(*dst, listBoxDst->getSelectedIndex(),
1768 													   *src, listBoxSrc->getSelectedIndex());
1769 					break;
1770 				case INSTRUMENT_CHOOSER_SWAP:
1771 					this->moduleEditor->swapInstruments(*dst, listBoxDst->getSelectedIndex(),
1772 														*src, listBoxSrc->getSelectedIndex());
1773 					break;
1774 				default:
1775 					ASSERT(false);
1776 			}
1777 
1778 			if (messageBoxButtonID == PP_MESSAGEBOX_BUTTON_USER3)
1779 			{
1780 				pp_int32 index = listBoxDst->getSelectedIndex()+1;
1781 				listBoxDst->setSelectedIndex(index, false, true);
1782 				index = listBoxSrc->getSelectedIndex()+1;
1783 				listBoxSrc->setSelectedIndex(index, false, true);
1784 			}
1785 
1786 			break;
1787 		}
1788 
1789 		case PP_MESSAGEBOX_BUTTON_USER2:
1790 		case PP_MESSAGEBOX_BUTTON_USER4:
1791 		{
1792 			PPListBox* listBoxSrcSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
1793 			PPListBox* listBoxDstSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
1794 
1795 			switch (messageBoxID)
1796 			{
1797 				case INSTRUMENT_CHOOSER_COPY:
1798 					this->moduleEditor->copySample(*dst, listBoxDst->getSelectedIndex(), listBoxDstSmp->getSelectedIndex(),
1799 												   *src, listBoxSrc->getSelectedIndex(), listBoxSrcSmp->getSelectedIndex());
1800 					break;
1801 				case INSTRUMENT_CHOOSER_SWAP:
1802 					this->moduleEditor->swapSamples(*dst, listBoxDst->getSelectedIndex(), listBoxDstSmp->getSelectedIndex(),
1803 													*src, listBoxSrc->getSelectedIndex(), listBoxSrcSmp->getSelectedIndex());
1804 					break;
1805 				default:
1806 					ASSERT(false);
1807 			}
1808 
1809 			if (messageBoxButtonID == PP_MESSAGEBOX_BUTTON_USER4)
1810 			{
1811 				pp_int32 index = listBoxDstSmp->getSelectedIndex()+1;
1812 				listBoxDstSmp->setSelectedIndex(index, false, true);
1813 				index = listBoxSrcSmp->getSelectedIndex()+1;
1814 				listBoxSrcSmp->setSelectedIndex(index, false, true);
1815 			}
1816 			break;
1817 		}
1818 
1819 		// swap source/destination list boxes
1820 		case PP_MESSAGEBOX_BUTTON_USER5:
1821 		{
1822 			if (listBoxSrcModule == NULL || listBoxDstModule == NULL)
1823 				return true;
1824 
1825 			PPListBox* listBoxSrcSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
1826 			PPListBox* listBoxDstSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
1827 
1828 			pp_int32 srcModIndex = listBoxSrcModule->getSelectedIndex();
1829 			pp_int32 dstModIndex = listBoxDstModule->getSelectedIndex();
1830 
1831 			pp_int32 srcInsIndex = listBoxSrc->getSelectedIndex();
1832 			pp_int32 dstInsIndex = listBoxDst->getSelectedIndex();
1833 
1834 			pp_int32 srcSmpIndex = listBoxSrcSmp->getSelectedIndex();
1835 			pp_int32 dstSmpIndex = listBoxDstSmp->getSelectedIndex();
1836 
1837 			listBoxSrcModule->setSelectedIndex(dstModIndex, false);
1838 			listBoxDstModule->setSelectedIndex(srcModIndex, false);
1839 
1840 			updateInstrumentChooser(false);
1841 
1842 			listBoxSrc->setSelectedIndex(dstInsIndex, false);
1843 			listBoxDst->setSelectedIndex(srcInsIndex, false);
1844 
1845 			listBoxSrcSmp->setSelectedIndex(dstSmpIndex, false);
1846 			listBoxDstSmp->setSelectedIndex(srcSmpIndex, false);
1847 
1848 			updateInstrumentChooser(true);
1849 			screen->paint();
1850 			return true;
1851 		}
1852 
1853 		// one more instrument in the source module
1854 		case PP_MESSAGEBOX_BUTTON_USER10:
1855 		{
1856 			src->allocateInstrument();
1857 			break;
1858 		}
1859 
1860 		// delete one instrument in the source module
1861 		case PP_MESSAGEBOX_BUTTON_USER11:
1862 		{
1863 			src->freeInstrument();
1864 			break;
1865 		}
1866 
1867 		// one more instrument in the destination module
1868 		case PP_MESSAGEBOX_BUTTON_USER12:
1869 		{
1870 			dst->allocateInstrument();
1871 			break;
1872 		}
1873 
1874 		// delete one instrument in the destination module
1875 		case PP_MESSAGEBOX_BUTTON_USER13:
1876 		{
1877 			dst->freeInstrument();
1878 			break;
1879 		}
1880 
1881 		case PP_MESSAGEBOX_BUTTON_USER6:
1882 		{
1883 			// play source
1884 			SamplePlayer samplePlayer(*src, *playerController);
1885 			PPListBox* listBoxSrcSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC2));
1886 
1887 			samplePlayer.playSample(listBoxSrc->getSelectedIndex(),
1888 									listBoxSrcSmp->getSelectedIndex(),
1889 									sectionSamples->getCurrentSamplePlayNote());
1890 			return true;
1891 		}
1892 
1893 		case PP_MESSAGEBOX_BUTTON_USER7:
1894 		{
1895 			// play dest
1896 			SamplePlayer samplePlayer(*dst, *playerController);
1897 			PPListBox* listBoxDstSmp = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST2));
1898 
1899 			samplePlayer.playSample(listBoxDst->getSelectedIndex(),
1900 									listBoxDstSmp->getSelectedIndex(),
1901 									sectionSamples->getCurrentSamplePlayNote());
1902 			return true;
1903 		}
1904 
1905 		default:
1906 			return false;
1907 	}
1908 
1909 	updateInstrumentChooser(false);
1910 
1911 	if (listBoxInstruments->getSelectedIndex() == listBoxDst->getSelectedIndex() &&
1912 		dst == this->moduleEditor)
1913 	{
1914 		updateInstrumentsListBox(false);
1915 		sectionInstruments->resetEnvelopeEditor();
1916 		sectionSamples->resetSampleEditor();
1917 	}
1918 
1919 	PPButton* button = static_cast<PPButton*>(container->getControlByID(PP_MESSAGEBOX_BUTTON_CANCEL));
1920 	button->setText("Done");
1921 
1922 	sectionSamples->updateAfterLoad();
1923 
1924 	screen->paint();
1925 	return true;
1926 }
1927 
handleQuit()1928 void Tracker::handleQuit()
1929 {
1930 	if (sectionSettings->isVisible())
1931 		sectionSettings->cancelSettings();
1932 	screen->shutDown();
1933 }
1934 
messageBoxEventListener(pp_int32 messageBoxID,pp_int32 messageBoxButtonID)1935 bool Tracker::messageBoxEventListener(pp_int32 messageBoxID, pp_int32 messageBoxButtonID)
1936 {
1937 	switch (messageBoxID)
1938 	{
1939 		case MESSAGEBOX_REALLYQUIT:
1940 		{
1941 			if (messageBoxButtonID == PP_MESSAGEBOX_BUTTON_YES)
1942 			{
1943 				handleQuit();
1944 			}
1945 			break;
1946 		}
1947 
1948 		case INSTRUMENT_CHOOSER_COPY:
1949 		case INSTRUMENT_CHOOSER_SWAP:
1950 		{
1951 			if (swapAndCopyHandler(messageBoxID, messageBoxButtonID))
1952 				return false;
1953 
1954 			break;
1955 		}
1956 
1957 		case MESSAGEBOX_INSREMAP:
1958 		{
1959 			switch (messageBoxButtonID)
1960 			{
1961 				case PP_MESSAGEBOX_BUTTON_USER1:
1962 				case PP_MESSAGEBOX_BUTTON_USER2:
1963 				case PP_MESSAGEBOX_BUTTON_USER3:
1964 				case PP_MESSAGEBOX_BUTTON_USER4:
1965 				{
1966 					PPContainer* container = static_cast<PPContainer*>(screen->getModalControl());
1967 					PPListBox* listBoxSrc = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_SRC));
1968 					PPListBox* listBoxDst = static_cast<PPListBox*>(container->getControlByID(INSTRUMENT_CHOOSER_LIST_DST));
1969 
1970 					pp_int32 newIns = listBoxDst->getSelectedIndex()+1;
1971 					pp_int32 oldIns = listBoxSrc->getSelectedIndex()+1;
1972 
1973 					pp_int32 res = 0;
1974 
1975 					switch (messageBoxButtonID)
1976 					{
1977 						case PP_MESSAGEBOX_BUTTON_USER1:
1978 							res = getPatternEditor()->insRemapTrack(oldIns, newIns);
1979 							break;
1980 						case PP_MESSAGEBOX_BUTTON_USER2:
1981 							res = getPatternEditor()->insRemapPattern(oldIns, newIns);
1982 							break;
1983 						case PP_MESSAGEBOX_BUTTON_USER3:
1984 							res = moduleEditor->insRemapSong(oldIns, newIns);
1985 							break;
1986 						case PP_MESSAGEBOX_BUTTON_USER4:
1987 							res = getPatternEditor()->insRemapSelection(oldIns, newIns);
1988 							break;
1989 					}
1990 
1991 					char buffer[100];
1992 					sprintf(buffer, "%i Instruments have been remapped", res);
1993 					showMessageBox(MESSAGEBOX_UNIVERSAL, buffer, MessageBox_OK);
1994 
1995 					screen->paint();
1996 					return false;
1997 				}
1998 			}
1999 
2000 			break;
2001 		}
2002 	}
2003 
2004 	return true;
2005 }
2006 
2007 ///////////////////////////////////////////////////////////
2008 // check if editing of any textfield is currently
2009 // performed, if so we are not allowed to play instruments
2010 ///////////////////////////////////////////////////////////
isActiveEditing()2011 bool Tracker::isActiveEditing()
2012 {
2013 	// check for focus of song title edit field
2014 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_ABOUT));
2015 	PPListBox* listBox = static_cast<PPListBox*>(container->getControlByID(LISTBOX_SONGTITLE));
2016 
2017 	if (screen->hasFocus(container) && listBox->isEditing())
2018 		return true;
2019 
2020 	container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_INSTRUMENTLIST));
2021 	listBox = listBoxInstruments;
2022 
2023 	if (screen->hasFocus(container) && listBox->isEditing())
2024 		return true;
2025 
2026 	container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_INSTRUMENTLIST));
2027 	listBox = listBoxSamples;
2028 
2029 	if (screen->hasFocus(container) && listBox->isEditing())
2030 		return true;
2031 
2032 	if (sectionDiskMenu->isActiveEditing())
2033 		return true;
2034 
2035 	return false;
2036 }
2037 
ensureSongStopped(bool bResetMainVolume,bool suspend)2038 void Tracker::ensureSongStopped(bool bResetMainVolume, bool suspend)
2039 {
2040 	updatePianoControl(sectionInstruments->getPianoControl());
2041 #ifdef __LOWRES__
2042 	{
2043 		PianoControl* pianoControl = static_cast<PianoControl*>(inputContainerCurrent->getControlByID(PIANO_CONTROL));
2044 		updatePianoControl(pianoControl);
2045 	}
2046 #endif
2047 	playerLogic->ensureSongStopped(bResetMainVolume, suspend);
2048 }
2049 
ensureSongPlaying(bool continuePlaying)2050 void Tracker::ensureSongPlaying(bool continuePlaying)
2051 {
2052 	playerLogic->ensureSongPlaying(continuePlaying);
2053 }
2054 
initPlayback()2055 void Tracker::initPlayback()
2056 {
2057 	resetStateMemories();
2058 
2059 	playerController->resetPlayTimeCounter();
2060 
2061 	recorderLogic->init();
2062 }
2063 
setChanged()2064 void Tracker::setChanged()
2065 {
2066 	moduleEditor->setChanged();
2067 	updateWindowTitle();
2068 }
2069 
shouldFollowSong()2070 bool Tracker::shouldFollowSong()
2071 {
2072 	if (playerController->getNextOrderToPlay() != -1 ||
2073 		playerController->getNextPatternToPlay() != -1)
2074 		return false;
2075 
2076 	return getFollowSong();
2077 }
2078 
getFollowSong()2079 bool Tracker::getFollowSong()
2080 {
2081 	return followSong;
2082 }
2083 
setFollowSong(bool b,bool repaint)2084 void Tracker::setFollowSong(bool b, bool repaint/* = true*/)
2085 {
2086 	followSong = b;
2087 	updateAboutToggleButton(BUTTON_ABOUT_FOLLOWSONG, b, repaint);
2088 }
2089 
getProspectiveMode()2090 bool Tracker::getProspectiveMode()
2091 {
2092 	return getPatternEditorControl()->getProspective();
2093 }
2094 
setProspectiveMode(bool b,bool repaint)2095 void Tracker::setProspectiveMode(bool b, bool repaint/* = true*/)
2096 {
2097 	getPatternEditorControl()->setProspective(b);
2098 	updateAboutToggleButton(BUTTON_ABOUT_PROSPECTIVE, b, repaint);
2099 }
2100 
getCursorWrapAround()2101 bool Tracker::getCursorWrapAround()
2102 {
2103 	return getPatternEditorControl()->getWrapAround();
2104 }
2105 
setCursorWrapAround(bool b,bool repaint)2106 void Tracker::setCursorWrapAround(bool b, bool repaint/* = true*/)
2107 {
2108 	getPatternEditorControl()->setWrapAround(b);
2109 	updateAboutToggleButton(BUTTON_ABOUT_WRAPCURSOR, b, repaint);
2110 }
2111 
setLiveSwitch(bool b,bool repaint)2112 void Tracker::setLiveSwitch(bool b, bool repaint/* = true*/)
2113 {
2114 	playerLogic->setLiveSwitch(b);
2115 	updateAboutToggleButton(BUTTON_ABOUT_LIVESWITCH, b, repaint);
2116 }
2117 
updateSongRow(bool checkFollowSong)2118 void Tracker::updateSongRow(bool checkFollowSong/* = true*/)
2119 {
2120 	if (checkFollowSong && !shouldFollowSong())
2121 		return;
2122 
2123 	mp_sint32 row = getPatternEditorControl()->getCurrentRow();
2124 	mp_sint32 pos = listBoxOrderList->getSelectedIndex();
2125 
2126 	playerController->setPatternPos(pos, row);
2127 }
2128 
selectInstrument(pp_int32 instrument)2129 void Tracker::selectInstrument(pp_int32 instrument)
2130 {
2131 	// instrument index starts at 0 in the module editor
2132 	// but from 1 everywhere else
2133 	moduleEditor->setCurrentInstrumentIndex(instrument-1);
2134 
2135 	getPatternEditorControl()->setCurrentInstrument(instrument);
2136 
2137 	sectionTranspose->setCurrentInstrument(instrument, false);
2138 
2139 	updateSamplesListBox(false);
2140 
2141 	// update instrument/sample editor
2142 	// important: sample editor first => will reload sample into sample editor
2143 	updateSampleEditor(false);
2144 	// update instrument/sample editor
2145 	sectionInstruments->update(false);
2146 
2147 	for (pp_int32 i = 0; i < sections->size(); i++)
2148 		sections->get(i)->notifyInstrumentSelect(instrument);
2149 }
2150 
fillInstrumentListBox(PPListBox * listBox,ModuleEditor * moduleEditor)2151 void Tracker::fillInstrumentListBox(PPListBox* listBox, ModuleEditor* moduleEditor/* = NULL*/)
2152 {
2153 	if (moduleEditor == NULL)
2154 		moduleEditor = this->moduleEditor;
2155 	char name[MP_MAXTEXT];
2156 	for (pp_int32 j = 0; j < moduleEditor->getNumInstruments(); j++)
2157 	{
2158 		memset(name, 0, sizeof(name));
2159 		moduleEditor->getInstrumentName(j, name, ModuleEditor::MAX_INSTEXT);
2160 		listBox->addItem(name);
2161 	}
2162 }
2163 
fillSampleListBox(PPListBox * listBox,pp_int32 insIndex,ModuleEditor * moduleEditor)2164 void Tracker::fillSampleListBox(PPListBox* listBox, pp_int32 insIndex, ModuleEditor* moduleEditor/* = NULL*/)
2165 {
2166 	if (moduleEditor == NULL)
2167 		moduleEditor = this->moduleEditor;
2168 	for (pp_int32 j = 0; j < moduleEditor->getNumSamples(insIndex); j++)
2169 	{
2170 		char name[MP_MAXTEXT];
2171 		moduleEditor->getSampleName(insIndex, j, name, ModuleEditor::MAX_SMPTEXT);
2172 		listBox->addItem(name);
2173 	}
2174 }
2175 
fillModuleListBox(PPListBox * listBox)2176 void Tracker::fillModuleListBox(PPListBox* listBox)
2177 {
2178 #ifndef __LOWRES__
2179 	TabHeaderControl* tabHeader = static_cast<TabHeaderControl*>(screen->getControlByID(TABHEADER_CONTROL));
2180 	for (pp_int32 i = 0; i < (signed)tabHeader->getNumTabs(); i++)
2181 	{
2182 		TabTitleProvider tabTitleProvider(*tabManager->getModuleEditorFromTabIndex(i));
2183 		listBox->addItem(tabTitleProvider.getTabTitle());
2184 	}
2185 #endif
2186 }
2187 
rearrangePatternEditorControl()2188 void Tracker::rearrangePatternEditorControl()
2189 {
2190 #ifdef __LOWRES__
2191 	PatternEditorControl* control = getPatternEditorControl();
2192 
2193 	if (control)
2194 	{
2195 		PPPoint location = control->getLocation();
2196 
2197 		pp_int32 height = inputContainerCurrent->getLocation().y;
2198 
2199 		control->setSize(PPSize(screen->getWidth(), height - location.y));
2200 		if (inputContainerCurrent)
2201 			inputContainerCurrent->show(true);
2202 	}
2203 #endif
2204 }
2205 
rearrangePatternEditorControlOrInstrumentContainer()2206 void Tracker::rearrangePatternEditorControlOrInstrumentContainer()
2207 {
2208 	if (sectionDiskMenu->isDiskMenuVisible())
2209 		sectionDiskMenu->resizeInstrumentContainer();
2210 	else
2211 		rearrangePatternEditorControl();
2212 }
2213 
showScopes(bool visible,pp_uint32 style)2214 void Tracker::showScopes(bool visible, pp_uint32 style)
2215 {
2216 	if (!scopesControl)
2217 		return;
2218 
2219 	scopesControl->setAppearance((ScopesControl::AppearanceTypes)style);
2220 
2221 #ifndef __LOWRES__
2222 	if (visible && scopesControl->isVisible())
2223 		return;
2224 
2225 	if (!visible && !scopesControl->isVisible())
2226 		return;
2227 
2228 	PPSize size = getPatternEditorControl()->getSize();
2229 	PPPoint location = getPatternEditorControl()->getLocation();
2230 
2231 	if (visible)
2232 	{
2233 		scopesControl->hide(false);
2234 		size.height -= scopesControl->getSize().height;
2235 		location.y += scopesControl->getSize().height;
2236 		getPatternEditorControl()->setSize(size);
2237 		getPatternEditorControl()->setLocation(location);
2238 	}
2239 	else
2240 	{
2241 		scopesControl->hide(true);
2242 		size.height += scopesControl->getSize().height;
2243 		location.y -= scopesControl->getSize().height;
2244 		getPatternEditorControl()->setSize(size);
2245 		getPatternEditorControl()->setLocation(location);
2246 	}
2247 
2248 	// store settings
2249 	settingsDatabase->store("SCOPES", (visible ? 1 : 0) | (style << 1));
2250 
2251 	// if config menu is visible, update the toggles
2252 	if (sectionSettings->isVisible())
2253 		sectionSettings->update(false);
2254 
2255 #endif
2256 
2257 	screen->paint();
2258 }
2259 
setInputControl(SIPs sip)2260 void Tracker::setInputControl(SIPs sip)
2261 {
2262 	switch (sip)
2263 	{
2264 		case SIPDefault:
2265 		{
2266 			inputContainerCurrent = inputContainerDefault;
2267 			inputContainerExtended->hide(true);
2268 			inputContainerCurrent->hide(false);
2269 			break;
2270 		}
2271 
2272 		case SIPExtended:
2273 		{
2274 			inputContainerCurrent = inputContainerExtended;
2275 			inputContainerDefault->hide(true);
2276 			inputContainerCurrent->hide(false);
2277 			break;
2278 		}
2279 	}
2280 
2281 	rearrangePatternEditorControlOrInstrumentContainer();
2282 
2283 	screen->update();
2284 }
2285 
expandOrderlist(bool b)2286 void Tracker::expandOrderlist(bool b)
2287 {
2288 	extendedOrderlist = b;
2289 
2290 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_ORDERLIST));
2291 
2292 	PPPoint p = container->getLocation();
2293 	pp_int32 x = p.x;
2294 	pp_int32 y = p.y;
2295 
2296 	ASSERT(container);
2297 
2298 	if (b)
2299 	{
2300 		container->getControlByID(BUTTON_ORDERLIST_SONGLENGTH_PLUS)->setLocation(PPPoint(x+2 + 78-2, y+2+12+12+12));
2301 		container->getControlByID(BUTTON_ORDERLIST_SONGLENGTH_MINUS)->setLocation(PPPoint(x+2 + 78-2 + 17, y+2+12+12+12));
2302 
2303 		container->getControlByID(BUTTON_ORDERLIST_REPEAT_PLUS)->hide(true);
2304 		container->getControlByID(BUTTON_ORDERLIST_REPEAT_MINUS)->hide(true);
2305 		container->getControlByID(STATICTEXT_ORDERLIST_SONGLENGTH)->setLocation(PPPoint(x+ 8*12, y+2+12+12+12+2+12));
2306 		container->getControlByID(STATICTEXT_ORDERLIST_REPEAT)->hide(true);
2307 		container->getControlByID(0)->hide(true);
2308 		container->getControlByID(1)->hide(false);
2309 		container->getControlByID(2)->hide(true);
2310 		static_cast<PPButton*>(container->getControlByID(BUTTON_ORDERLIST_EXTENT))->setText(TrackerConfig::stringButtonExtended);
2311 
2312 		PPSize size = container->getControlByID(LISTBOX_ORDERLIST)->getSize();
2313 		size.height = 60;
2314 		container->getControlByID(LISTBOX_ORDERLIST)->setSize(size);
2315 	}
2316 	else
2317 	{
2318 		container->getControlByID(BUTTON_ORDERLIST_SONGLENGTH_PLUS)->setLocation(PPPoint(x+2 + 78-2, y+2+12+12+12));
2319 		container->getControlByID(BUTTON_ORDERLIST_SONGLENGTH_MINUS)->setLocation(PPPoint(x+2 + 78-2 + 17, y+2+12+12+12));
2320 
2321 		container->getControlByID(BUTTON_ORDERLIST_REPEAT_PLUS)->hide(false);
2322 		container->getControlByID(BUTTON_ORDERLIST_REPEAT_MINUS)->hide(false);
2323 		container->getControlByID(STATICTEXT_ORDERLIST_SONGLENGTH)->setLocation(PPPoint(x+2 + 8*7, y+2+12+12+12+2));
2324 		container->getControlByID(STATICTEXT_ORDERLIST_REPEAT)->hide(false);
2325 		container->getControlByID(0)->hide(false);
2326 		container->getControlByID(1)->hide(true);
2327 		container->getControlByID(2)->hide(false);
2328 		static_cast<PPButton*>(container->getControlByID(BUTTON_ORDERLIST_EXTENT))->setText(TrackerConfig::stringButtonCollapsed);
2329 
2330 		PPSize size = container->getControlByID(LISTBOX_ORDERLIST)->getSize();
2331 		size.height = 36;
2332 		container->getControlByID(LISTBOX_ORDERLIST)->setSize(size);
2333 	}
2334 
2335 }
2336 
flipSpeedSection()2337 void Tracker::flipSpeedSection()
2338 {
2339 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_SPEED));
2340 	ASSERT(container);
2341 
2342 	PPControl* control = container->getControlByID(STATICTEXT_SPEED_OCTAVE);
2343 	ASSERT(control);
2344 
2345 	pp_int32 dy = container->getControlByID(STATICTEXT_SPEED_SPEED)->getLocation().y -
2346 				  container->getControlByID(STATICTEXT_SPEED_BPM)->getLocation().y;
2347 
2348 	if (control->isHidden())
2349 	{
2350 		// Show octave integer field
2351 		control->hide(false);
2352 		// Show octave description text ("Oct")
2353 		control = container->getControlByID(STATICTEXT_SPEED_OCTAVE_DESC);
2354 		control->hide(false);
2355 		// Show octave minus button
2356 		control = container->getControlByID(BUTTON_OCTAVE_MINUS);
2357 		control->hide(false);
2358 		// Show octave plus button
2359 		control = container->getControlByID(BUTTON_OCTAVE_PLUS);
2360 		control->hide(false);
2361 
2362 		// show mainvol text
2363 		control = container->getControlByID(STATICTEXT_SPEED_MAINVOL);
2364 		control->hide(false);
2365 		// show mainvol description text ("Mainvol")
2366 		control = container->getControlByID(STATICTEXT_SPEED_MAINVOL_DESC);
2367 		control->hide(false);
2368 
2369 		// hide BPM text fields + buttons
2370 		control = container->getControlByID(STATICTEXT_SPEED_BPM);
2371 		control->hide(true);
2372 		control = container->getControlByID(STATICTEXT_SPEED_BPM_DESC);
2373 		control->hide(true);
2374 		control = container->getControlByID(BUTTON_BPM_MINUS);
2375 		control->hide(true);
2376 		control = container->getControlByID(BUTTON_BPM_PLUS);
2377 		control->hide(true);
2378 
2379 		// hide tick speed text fields + buttons
2380 		control = container->getControlByID(BUTTON_SPEED_MINUS);
2381 		control->hide(true);
2382 		control = container->getControlByID(BUTTON_SPEED_PLUS);
2383 		control->hide(true);
2384 		control = container->getControlByID(STATICTEXT_SPEED_SPEED);
2385 		control->hide(true);
2386 		control = container->getControlByID(STATICTEXT_SPEED_SPEED_DESC);
2387 		control->hide(true);
2388 
2389 		// Move pattern add text fields + buttons one row upwards
2390 		control = container->getControlByID(STATICTEXT_SPEED_PATTERNADD);
2391 		PPPoint p = control->getLocation();
2392 		p.y-=dy;
2393 		control->setLocation(p);
2394 		control = container->getControlByID(STATICTEXT_SPEED_PATTERNADD_DESC);
2395 		p = control->getLocation();
2396 		p.y-=dy;
2397 		control->setLocation(p);
2398 		control = container->getControlByID(BUTTON_ADD_PLUS);
2399 		p = control->getLocation();
2400 		p.y-=dy;
2401 		control->setLocation(p);
2402 		control = container->getControlByID(BUTTON_ADD_MINUS);
2403 		p = control->getLocation();
2404 		p.y-=dy;
2405 		control->setLocation(p);
2406 	}
2407 	else
2408 	{
2409 		// The hide octave texts + buttons
2410 		control->hide(true);
2411 		control = container->getControlByID(STATICTEXT_SPEED_OCTAVE_DESC);
2412 		control->hide(true);
2413 		control = container->getControlByID(BUTTON_OCTAVE_MINUS);
2414 		control->hide(true);
2415 		control = container->getControlByID(BUTTON_OCTAVE_PLUS);
2416 		control->hide(true);
2417 
2418 		// hide mainvol text
2419 		control = container->getControlByID(STATICTEXT_SPEED_MAINVOL);
2420 		control->hide(true);
2421 		control = container->getControlByID(STATICTEXT_SPEED_MAINVOL_DESC);
2422 		control->hide(true);
2423 
2424 		// show bpm text + buttons
2425 		control = container->getControlByID(STATICTEXT_SPEED_BPM);
2426 		control->hide(false);
2427 		control = container->getControlByID(STATICTEXT_SPEED_BPM_DESC);
2428 		control->hide(false);
2429 		control = container->getControlByID(BUTTON_BPM_MINUS);
2430 		control->hide(false);
2431 		control = container->getControlByID(BUTTON_BPM_PLUS);
2432 		control->hide(false);
2433 
2434 		// show tick speed text + buttons
2435 		control = container->getControlByID(BUTTON_SPEED_MINUS);
2436 		control->hide(false);
2437 		control = container->getControlByID(BUTTON_SPEED_PLUS);
2438 		control->hide(false);
2439 		control = container->getControlByID(STATICTEXT_SPEED_SPEED);
2440 		control->hide(false);
2441 		control = container->getControlByID(STATICTEXT_SPEED_SPEED_DESC);
2442 		control->hide(false);
2443 
2444 		// move pattern add text one row downwards
2445 		control = container->getControlByID(STATICTEXT_SPEED_PATTERNADD);
2446 		PPPoint p = control->getLocation();
2447 		p.y+=dy;
2448 		control->setLocation(p);
2449 		control = container->getControlByID(STATICTEXT_SPEED_PATTERNADD_DESC);
2450 		p = control->getLocation();
2451 		p.y+=dy;
2452 		control->setLocation(p);
2453 		control = container->getControlByID(BUTTON_ADD_PLUS);
2454 		p = control->getLocation();
2455 		p.y+=dy;
2456 		control->setLocation(p);
2457 		control = container->getControlByID(BUTTON_ADD_MINUS);
2458 		p = control->getLocation();
2459 		p.y+=dy;
2460 		control->setLocation(p);
2461 	}
2462 }
2463 
enableInstrument(bool b)2464 void Tracker::enableInstrument(bool b)
2465 {
2466 	getPatternEditorControl()->enableInstrument(b);
2467 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_INSTRUMENTLIST));
2468 	ASSERT(container);
2469 	PPButton* button = static_cast<PPButton*>(container->getControlByID(BUTTON_INSTRUMENT));
2470 	ASSERT(button);
2471 	button->setPressed(b);
2472 	listBoxInstruments->setOnlyShowIndexSelection(!b);
2473 	screen->paintControl(listBoxInstruments);
2474 	screen->paintControl(button);
2475 }
2476 
commitListBoxChanges()2477 void Tracker::commitListBoxChanges()
2478 {
2479 	if (listBoxInstruments->isEditing())
2480 		listBoxInstruments->commitChanges();
2481 
2482 	if (listBoxSamples->isEditing())
2483 		listBoxSamples->commitChanges();
2484 }
2485 
getCurrentSelectedSampleSaveType()2486 FileTypes Tracker::getCurrentSelectedSampleSaveType()
2487 {
2488 	return (FileTypes)sectionDiskMenu->getCurrentSelectedSampleSaveType();
2489 }
2490 
fileTypeToHint(FileTypes type)2491 pp_uint32 Tracker::fileTypeToHint(FileTypes type)
2492 {
2493 	switch (type)
2494 	{
2495 		case FileTypes::FileTypeSongAllModules:
2496 		case FileTypes::FileTypeSongMOD:
2497 		case FileTypes::FileTypeSongXM:
2498 			return DecompressorBase::HintModules;
2499 
2500 		case FileTypes::FileTypePatternXP:
2501 			return DecompressorBase::HintPatterns;
2502 
2503 		case FileTypes::FileTypeTrackXT:
2504 			return DecompressorBase::HintTracks;
2505 
2506 		case FileTypes::FileTypeSongAllInstruments:
2507 		case FileTypes::FileTypeInstrumentXI:
2508 			return DecompressorBase::HintInstruments;
2509 
2510 		case FileTypes::FileTypeSongAllSamples:
2511 		case FileTypes::FileTypeSampleWAV:
2512 		case FileTypes::FileTypeSampleIFF:
2513 			return DecompressorBase::HintSamples;
2514 
2515 		default:
2516 			return DecompressorBase::HintAll;
2517 
2518 	}
2519 }
2520 
prepareLoadSaveUI()2521 void Tracker::prepareLoadSaveUI()
2522 {
2523 #ifdef __LOWRES__
2524 		// The bottom section fills up the entire screen
2525 		// so we first need to hide the entire section before we can show the disk menu
2526 		screen->pauseUpdate(true);
2527 		sectionSwitcher->hideBottomSection();
2528 #endif
2529 }
2530 
finishLoadSaveUI()2531 void Tracker::finishLoadSaveUI()
2532 {
2533 #ifdef __LOWRES__
2534 		screen->pauseUpdate(false);
2535 		screen->paint();
2536 #endif
2537 }
2538 
loadGenericFileType(const PPSystemString & fileName)2539 bool Tracker::loadGenericFileType(const PPSystemString& fileName)
2540 {
2541 	// we need to find out what file type this is
2542 	// so we can do appropriate loading
2543 	FileIdentificator* fileIdentificator = new FileIdentificator(fileName);
2544 	FileIdentificator::FileTypes type = fileIdentificator->getFileType();
2545 	delete fileIdentificator;
2546 	// check for compression
2547 	if (type == FileIdentificator::FileTypeCompressed)
2548 	{
2549 		// if this is compressed, we try to uncompress it
2550 		// and choose that file type
2551 		PPSystemString tempFile(ModuleEditor::getTempFilename());
2552 		Decompressor decompressor(fileName);
2553 		if (decompressor.decompress(tempFile, (DecompressorBase::Hints)fileTypeToHint(FileTypes::FileTypeAllFiles)))
2554 		{
2555 			fileIdentificator = new FileIdentificator(tempFile);
2556 			type = fileIdentificator->getFileType();
2557 			delete fileIdentificator;
2558 			Decompressor::removeFile(tempFile);
2559 		}
2560 		else
2561 		{
2562 			showMessageBox(MESSAGEBOX_UNIVERSAL, "Unrecognized type/corrupt file", MessageBox_OK);
2563 			return false;
2564 		}
2565 	}
2566 
2567 	switch (type)
2568 	{
2569 		case FileIdentificator::FileTypeModule:
2570 			return loadTypeFromFile(FileTypes::FileTypeSongAllModules, fileName);
2571 		case FileIdentificator::FileTypeInstrument:
2572 			return loadTypeFromFile(FileTypes::FileTypeSongAllInstruments, fileName);
2573 		case FileIdentificator::FileTypeSample:
2574 			return loadTypeFromFile(FileTypes::FileTypeSongAllSamples, fileName);
2575 		case FileIdentificator::FileTypePattern:
2576 			return loadTypeFromFile(FileTypes::FileTypePatternXP, fileName);
2577 		case FileIdentificator::FileTypeTrack:
2578 			return loadTypeFromFile(FileTypes::FileTypeTrackXT, fileName);
2579 		default:
2580 			return false;
2581 	}
2582 }
2583 
prepareLoading(FileTypes eType,const PPSystemString & fileName,bool suspendPlayer,bool repaint,bool saveCheck)2584 bool Tracker::prepareLoading(FileTypes eType, const PPSystemString& fileName, bool suspendPlayer, bool repaint, bool saveCheck)
2585 {
2586 	loadingParameters.deleteFile = false;
2587 	loadingParameters.didOpenTab = false;
2588 	loadingParameters.eType = eType;
2589 	loadingParameters.filename = fileName;
2590 	loadingParameters.preferredFilename = fileName;
2591 	loadingParameters.suspendPlayer = suspendPlayer;
2592 	loadingParameters.repaint = repaint;
2593 
2594 	loadingParameters.res = true;
2595 
2596 	if (saveCheck && eType == FileTypes::FileTypeSongAllModules && !checkForChangesOpenModule())
2597 		return false;
2598 
2599 	loadingParameters.lastError = "Error while loading/unknown format";
2600 
2601 	signalWaitState(true);
2602 
2603 	if (eType == FileTypes::FileTypeSongAllModules &&
2604 		settingsDatabase->restore("TABS_LOADMODULEINNEWTAB")->getBoolValue() &&
2605 		(moduleEditor->hasChanged() || !moduleEditor->isEmpty()))
2606 	{
2607 		loadingParameters.didOpenTab = true;
2608 		tabManager->openNewTab();
2609 	}
2610 
2611 	bool rowPlay = playerController->isPlayingRowOnly();
2612 	loadingParameters.wasPlaying = rowPlay ? false : (playerController->isPlaying() || playerController->isPlayingPattern());
2613 	loadingParameters.wasPlayingPattern = rowPlay ? false : playerController->isPlayingPattern();
2614 
2615 	if (loadingParameters.suspendPlayer)
2616 	{
2617 #ifndef __LOWRES__
2618 		scopesControl->enable(false);
2619 #endif
2620 		playerController->suspendPlayer();
2621 	}
2622 
2623 	// check for compressed file type
2624 	FileIdentificator* fileIdentificator = new FileIdentificator(fileName);
2625 	FileIdentificator::FileTypes type = fileIdentificator->getFileType();
2626 	delete fileIdentificator;
2627 
2628 	if (type == FileIdentificator::FileTypeCompressed)
2629 	{
2630 		// if this is compressed, try to decompress
2631 		PPSystemString tempFile(ModuleEditor::getTempFilename());
2632 		Decompressor decompressor(fileName);
2633 		if (decompressor.decompress(tempFile, (DecompressorBase::Hints)fileTypeToHint(eType)))
2634 		{
2635 			// we compressed to a temporary file
2636 			// load that instead, but keep the original file name as preferred
2637 			// base name for the module we're going to edit
2638 			loadingParameters.preferredFilename = loadingParameters.filename;
2639 			loadingParameters.filename = tempFile;
2640 			// delete file after loading, it's temporary
2641 			loadingParameters.deleteFile = true;
2642 		}
2643 		else
2644 		{
2645 			loadingParameters.lastError = "Unrecognized type/corrupt file";
2646 			loadingParameters.res = false;
2647 			finishLoading();
2648 			return false;
2649 		}
2650 	}
2651 
2652 	return true;
2653 }
2654 
finishLoading()2655 bool Tracker::finishLoading()
2656 {
2657 	signalWaitState(false);
2658 
2659 	if (loadingParameters.repaint)
2660 	{
2661 		screen->paint();
2662 		updateWindowTitle(moduleEditor->getModuleFileName());
2663 	}
2664 
2665 	if (!loadingParameters.res && !loadingParameters.abortLoading)
2666 		showMessageBox(MESSAGEBOX_UNIVERSAL, loadingParameters.lastError, MessageBox_OK);
2667 
2668 	if (loadingParameters.suspendPlayer)
2669 	{
2670 		playerController->resumePlayer(true);
2671 		scopesControl->enable(true);
2672 	}
2673 
2674 	if (loadingParameters.deleteFile)
2675 		Decompressor::removeFile(loadingParameters.filename);
2676 
2677 	if (!loadingParameters.res && loadingParameters.didOpenTab)
2678 		tabManager->closeTab();
2679 
2680 	return loadingParameters.abortLoading ? true : loadingParameters.res;
2681 }
2682 
loadTypeFromFile(FileTypes eType,const PPSystemString & fileName,bool suspendPlayer,bool repaint,bool saveCheck)2683 bool Tracker::loadTypeFromFile(FileTypes eType, const PPSystemString& fileName, bool suspendPlayer/* = true*/, bool repaint/* = true*/, bool saveCheck/* = true*/)
2684 {
2685 	bool res = prepareLoading(eType, fileName, suspendPlayer, repaint, saveCheck);
2686 	if (!res)
2687 		return false;
2688 
2689 	switch (eType)
2690 	{
2691 		case FileTypes::FileTypeSongAllModules:
2692 		{
2693 			if (loadingParameters.preferredFilename.length())
2694 				loadingParameters.res = moduleEditor->openSong(loadingParameters.filename,
2695 				loadingParameters.preferredFilename);
2696 			else
2697 				loadingParameters.res = moduleEditor->openSong(loadingParameters.filename,
2698 				NULL);
2699 
2700 			updateAfterLoad(loadingParameters.res, loadingParameters.wasPlaying, loadingParameters.wasPlayingPattern);
2701 			break;
2702 		}
2703 
2704 		case FileTypes::FileTypePatternXP:
2705 		{
2706 			loadingParameters.res = getPatternEditor()->loadExtendedPattern(loadingParameters.filename);
2707 			updateSongInfo();
2708 			break;
2709 		}
2710 
2711 		case FileTypes::FileTypeTrackXT:
2712 		{
2713 			loadingParameters.res = getPatternEditor()->loadExtendedTrack(loadingParameters.filename);
2714 			updateSongInfo();
2715 			break;
2716 		}
2717 
2718 		case FileTypes::FileTypeSongAllInstruments:
2719 		{
2720 			loadingParameters.res = moduleEditor->loadInstrument(loadingParameters.filename, listBoxInstruments->getSelectedIndex());
2721 			sectionInstruments->updateAfterLoad();
2722 			break;
2723 		}
2724 
2725 		case FileTypes::FileTypeSongAllSamples:
2726 		{
2727 			pp_int32 numSampleChannels = moduleEditor->getNumSampleChannels(loadingParameters.filename);
2728 
2729 			pp_int32 chnIndex = 0;
2730 			if (numSampleChannels <= 0)
2731 			{
2732 				loadingParameters.res = false;
2733 				break;
2734 			}
2735 			else if (numSampleChannels > 1 &&
2736 					 !settingsDatabase->restore("AUTOMIXDOWNSAMPLES")->getIntValue())
2737 			{
2738 				if (dialog)
2739 					delete dialog;
2740 
2741 				if (responder)
2742 					delete responder;
2743 
2744 				responder = new SampleLoadChannelSelectionHandler(*this);
2745 				dialog = new DialogChannelSelector(screen, responder, PP_DEFAULT_ID, "Choose channel to load" PPSTR_PERIODS);
2746 
2747 				// Add names of sample channels to instrument box
2748 				for (pp_int32 i = 0; i < numSampleChannels; i++)
2749 					static_cast<DialogChannelSelector*>(dialog)->getListBox()->addItem(moduleEditor->getNameOfSampleChannel(loadingParameters.filename, i));
2750 
2751 				static_cast<SampleLoadChannelSelectionHandler*>(responder)->setCurrentFileName(loadingParameters.filename);
2752 				static_cast<SampleLoadChannelSelectionHandler*>(responder)->setPreferredFileName(loadingParameters.preferredFilename);
2753 				static_cast<SampleLoadChannelSelectionHandler*>(responder)->suspendPlayer = suspendPlayer;
2754 
2755 				signalWaitState(false);
2756 
2757 				dialog->show();
2758 				return true;
2759 			}
2760 			else if (numSampleChannels > 1 &&
2761 					 settingsDatabase->restore("AUTOMIXDOWNSAMPLES")->getIntValue())
2762 			{
2763 				chnIndex = -1;
2764 			}
2765 
2766 			if (loadingParameters.preferredFilename.length())
2767 				loadingParameters.res = moduleEditor->loadSample(loadingParameters.filename,
2768 																 listBoxInstruments->getSelectedIndex(),
2769 																 listBoxSamples->getSelectedIndex(),
2770 																 chnIndex,
2771 																 loadingParameters.preferredFilename);
2772 			else
2773 				loadingParameters.res = moduleEditor->loadSample(loadingParameters.filename,
2774 																 listBoxInstruments->getSelectedIndex(),
2775 																 listBoxSamples->getSelectedIndex(),
2776 																 chnIndex);
2777 
2778 			sectionSamples->updateAfterLoad();
2779 			break;
2780 		}
2781 
2782 	}
2783 
2784 	return finishLoading();
2785 }
2786 
2787 // load different things
loadTypeWithDialog(FileTypes eLoadType,bool suspendPlayer,bool repaint)2788 bool Tracker::loadTypeWithDialog(FileTypes eLoadType, bool suspendPlayer/* = true*/, bool repaint/* = true*/)
2789 {
2790 	FileExtProvider fileExtProvider;
2791 
2792 	PPOpenPanel* openPanel = NULL;
2793 
2794 	switch (eLoadType)
2795 	{
2796 		case FileTypes::FileTypeSongAllModules:
2797 		{
2798 			if (!checkForChangesOpenModule())
2799 				return false;
2800 			openPanel = new PPOpenPanel(screen, "Open Module");
2801 			openPanel->addExtensions(fileExtProvider.getModuleExtensions());
2802 			break;
2803 		}
2804 
2805 		case FileTypes::FileTypePatternXP:
2806 		{
2807 			openPanel = new PPOpenPanel(screen, "Open Extended Pattern");
2808 			openPanel->addExtensions(fileExtProvider.getPatternExtensions());
2809 			break;
2810 		}
2811 
2812 		case FileTypes::FileTypeTrackXT:
2813 		{
2814 			openPanel = new PPOpenPanel(screen, "Open Extended Track");
2815 			openPanel->addExtensions(fileExtProvider.getTrackExtensions());
2816 			break;
2817 		}
2818 
2819 		case FileTypes::FileTypeSongAllInstruments:
2820 		{
2821 			openPanel = new PPOpenPanel(screen, "Open Instrument");
2822 			openPanel->addExtensions(fileExtProvider.getInstrumentExtensions());
2823 			break;
2824 		}
2825 
2826 		case FileTypes::FileTypeSongAllSamples:
2827 		{
2828 			openPanel = new PPOpenPanel(screen, "Open Sample");
2829 			openPanel->addExtensions(fileExtProvider.getSampleExtensions());
2830 			break;
2831 		}
2832 	}
2833 
2834 	if (!openPanel)
2835 		return false;
2836 
2837 	bool res = true;
2838 
2839 	if (openPanel->runModal() == PPModalDialog::ReturnCodeOK)
2840 	{
2841 		PPSystemString file = openPanel->getFileName();
2842 
2843 		if (file.length())
2844 		{
2845 			PPSystemString fileName = file;
2846 			res = loadTypeFromFile(eLoadType, fileName, suspendPlayer, repaint, false);
2847 		}
2848 
2849 		delete openPanel;
2850 	}
2851 
2852 	return res;
2853 }
2854 
loadType(FileTypes eType)2855 void Tracker::loadType(FileTypes eType)
2856 {
2857 	if (useClassicBrowser)
2858 	{
2859 		prepareLoadSaveUI();
2860 
2861 		sectionDiskMenu->selectSaveType(eType);
2862 		eventKeyDownBinding_InvokeSectionDiskMenu();
2863 
2864 		finishLoadSaveUI();
2865 	}
2866 	else
2867 	{
2868 		loadTypeWithDialog(eType);
2869 	}
2870 }
2871 
prepareSavingWithDialog(FileTypes eSaveType)2872 bool Tracker::prepareSavingWithDialog(FileTypes eSaveType)
2873 {
2874 	FileExtProvider fileExtProvider;
2875 
2876 	currentSaveFileType = eSaveType;
2877 
2878 	switch (eSaveType)
2879 	{
2880 		case FileTypes::FileTypeSongMOD:
2881 		{
2882 			savePanel = new PPSavePanel(screen, "Save Protracker Module", moduleEditor->getModuleFileName(ModuleEditor::ModSaveTypeMOD));
2883 			savePanel->addExtension(fileExtProvider.getModuleExtension(FileExtProvider::ModuleExtensionMOD),
2884 									fileExtProvider.getModuleDescription(FileExtProvider::ModuleExtensionMOD));
2885 
2886 			pp_uint32 err = moduleEditor->getPTIncompatibilityCode();
2887 
2888 			if (err)
2889 			{
2890 				buildMODSaveErrorWarning(err);
2891 				return false;
2892 			}
2893 			break;
2894 		}
2895 
2896 		case FileTypes::FileTypeSongXM:
2897 			savePanel = new PPSavePanel(screen, "Save Extended Module", moduleEditor->getModuleFileName(ModuleEditor::ModSaveTypeXM));
2898 			savePanel->addExtension(fileExtProvider.getModuleExtension(FileExtProvider::ModuleExtensionXM),
2899 									fileExtProvider.getModuleDescription(FileExtProvider::ModuleExtensionXM));
2900 			break;
2901 
2902 		case FileTypes::FileTypePatternXP:
2903 		{
2904 			PPSystemString fileName = moduleEditor->getModuleFileName().stripExtension();
2905 			fileName.append(".xp");
2906 			savePanel = new PPSavePanel(screen, "Save Extended Pattern", fileName);
2907 			savePanel->addExtension(fileExtProvider.getPatternExtension(FileExtProvider::PatternExtensionXP),
2908 									fileExtProvider.getPatternDescription(FileExtProvider::PatternExtensionXP));
2909 			break;
2910 		}
2911 
2912 		case FileTypes::FileTypeTrackXT:
2913 		{
2914 			PPSystemString fileName = moduleEditor->getModuleFileName().stripExtension();
2915 			fileName.append(".xt");
2916 			savePanel = new PPSavePanel(screen, "Save Extended Track", fileName);
2917 			savePanel->addExtension(fileExtProvider.getTrackExtension(FileExtProvider::TrackExtensionXT),
2918 									fileExtProvider.getTrackDescription(FileExtProvider::TrackExtensionXT));
2919 			break;
2920 		}
2921 
2922 		case FileTypes::FileTypeInstrumentXI:
2923 		{
2924 			PPSystemString sampleFileName = moduleEditor->getInstrumentFileName(listBoxInstruments->getSelectedIndex());
2925 			sampleFileName.append(".xi");
2926 			savePanel = new PPSavePanel(screen, "Save Instrument", sampleFileName);
2927 			savePanel->addExtension(fileExtProvider.getInstrumentExtension(FileExtProvider::InstrumentExtensionXI),
2928 									fileExtProvider.getInstrumentDescription(FileExtProvider::InstrumentExtensionXI));
2929 			break;
2930 		}
2931 
2932 		case FileTypes::FileTypeSampleWAV:
2933 		{
2934 			PPSystemString sampleFileName = moduleEditor->getSampleFileName(listBoxInstruments->getSelectedIndex(), listBoxSamples->getSelectedIndex());
2935 			sampleFileName.append(".wav");
2936 			savePanel = new PPSavePanel(screen, "Save uncompressed WAV",sampleFileName);
2937 			savePanel->addExtension(fileExtProvider.getSampleExtension(FileExtProvider::SampleExtensionWAV),
2938 									fileExtProvider.getSampleDescription(FileExtProvider::SampleExtensionWAV));
2939 			break;
2940 		}
2941 
2942 		case FileTypes::FileTypeSampleIFF:
2943 		{
2944 			PPSystemString sampleFileName = moduleEditor->getSampleFileName(listBoxInstruments->getSelectedIndex(), listBoxSamples->getSelectedIndex());
2945 			sampleFileName.append(".iff");
2946 			savePanel = new PPSavePanel(screen, "Save uncompressed IFF",sampleFileName);
2947 			savePanel->addExtension(fileExtProvider.getSampleExtension(FileExtProvider::SampleExtensionIFF),
2948 									fileExtProvider.getSampleDescription(FileExtProvider::SampleExtensionIFF));
2949 			break;
2950 		}
2951 	}
2952 
2953 	return true;
2954 }
2955 
2956 // Save different things
saveTypeWithDialog(FileTypes eSaveType,EventListenerInterface * fileSystemChangedListener)2957 bool Tracker::saveTypeWithDialog(FileTypes eSaveType, EventListenerInterface* fileSystemChangedListener/* = NULL*/)
2958 {
2959 	if (savePanel == NULL)
2960 	{
2961 		if (!prepareSavingWithDialog(eSaveType))
2962 		{
2963 			this->fileSystemChangedListener = fileSystemChangedListener;
2964 			return true;
2965 		}
2966 
2967 		if (!savePanel)
2968 			return true;
2969 	}
2970 
2971 	bool res = true;
2972 
2973 	if (savePanel->runModal() == PPModalDialog::ReturnCodeOK)
2974 	{
2975 		PPSystemString file = savePanel->getFileName();
2976 
2977 		if (file.length())
2978 		{
2979 			loadingParameters.lastError = "Error while saving";
2980 
2981 			signalWaitState(true);
2982 
2983 			switch (eSaveType)
2984 			{
2985 				case FileTypes::FileTypeSongMOD:
2986 					commitListBoxChanges();
2987 					res = moduleEditor->saveSong(file, ModuleEditor::ModSaveTypeMOD);
2988 					break;
2989 
2990 				case FileTypes::FileTypeSongXM:
2991 					commitListBoxChanges();
2992 					res = moduleEditor->saveSong(file, ModuleEditor::ModSaveTypeXM);
2993 					break;
2994 
2995 				case FileTypes::FileTypeTrackXT:
2996 				{
2997 					res = getPatternEditor()->saveExtendedTrack(file);
2998 					break;
2999 				}
3000 
3001 				case FileTypes::FileTypePatternXP:
3002 				{
3003 					res = getPatternEditor()->saveExtendedPattern(file);
3004 					break;
3005 				}
3006 
3007 				case FileTypes::FileTypeInstrumentXI:
3008 					commitListBoxChanges();
3009 					res = moduleEditor->saveInstrument(file, listBoxInstruments->getSelectedIndex());
3010 					break;
3011 
3012 				case FileTypes::FileTypeSampleWAV:
3013 					commitListBoxChanges();
3014 					res = moduleEditor->saveSample(file, listBoxInstruments->getSelectedIndex(),
3015 												   listBoxSamples->getSelectedIndex(), ModuleEditor::SampleFormatTypeWAV);
3016 					break;
3017 
3018 				case FileTypes::FileTypeSampleIFF:
3019 					commitListBoxChanges();
3020 					res = moduleEditor->saveSample(file, listBoxInstruments->getSelectedIndex(),
3021 												   listBoxSamples->getSelectedIndex(), ModuleEditor::SampleFormatTypeIFF);
3022 					break;
3023 			}
3024 
3025 			if (res && fileSystemChangedListener)
3026 			{
3027 				PPEvent event(eFileSystemChanged);
3028 				fileSystemChangedListener->handleEvent(reinterpret_cast<PPObject*>(this), &event);
3029 			}
3030 
3031 			signalWaitState(false);
3032 			screen->paint();
3033 			updateWindowTitle(moduleEditor->getModuleFileName());
3034 		}
3035 
3036 	}
3037 
3038 	if (!res)
3039 		showMessageBox(MESSAGEBOX_UNIVERSAL, loadingParameters.lastError, MessageBox_OK);
3040 
3041 	delete savePanel;
3042 	savePanel = NULL;
3043 	fileSystemChangedListener = NULL;
3044 
3045 	return res;
3046 }
3047 
saveCurrentModuleAsSelectedType()3048 bool Tracker::saveCurrentModuleAsSelectedType()
3049 {
3050 	signalWaitState(true);
3051 
3052 	loadingParameters.lastError = "Error while saving.\nFile might be write protected.";
3053 
3054 	pp_int32 res = moduleEditor->saveSong(moduleEditor->getModuleFileNameFull(), moduleEditor->getSaveType());
3055 	signalWaitState(false);
3056 
3057 	if (!res)
3058 		showMessageBoxSized(MESSAGEBOX_UNIVERSAL, loadingParameters.lastError, MessageBox_OK, -1, -1);
3059 
3060 	screen->paint();
3061 
3062 	if (res)
3063 		updateWindowTitle();
3064 
3065 	return res == 0;
3066 }
3067 
saveType(FileTypes eType)3068 void Tracker::saveType(FileTypes eType)
3069 {
3070 	if (useClassicBrowser)
3071 	{
3072 		prepareLoadSaveUI();
3073 
3074 		sectionDiskMenu->selectSaveType(eType);
3075 
3076 		if (eType == FileTypes::FileTypeSongWAV)
3077 			sectionDiskMenu->setModuleTypeAdjust(false);
3078 
3079 		eventKeyDownBinding_InvokeSectionDiskMenu();
3080 
3081 		if (eType == FileTypes::FileTypeSongWAV)
3082 			sectionDiskMenu->setModuleTypeAdjust(true);
3083 
3084 		finishLoadSaveUI();
3085 	}
3086 	else
3087 	{
3088 		saveTypeWithDialog(eType);
3089 	}
3090 }
3091 
save()3092 void Tracker::save()
3093 {
3094 	if (TrackerConfig::untitledSong.compareTo(moduleEditor->getModuleFileNameFull().stripExtension()) == 0)
3095 	{
3096 		saveAs();
3097 	}
3098 	else
3099 	{
3100 		if (moduleEditor->getSaveType() == ModuleEditor::ModSaveTypeMOD)
3101 		{
3102 			pp_uint32 err = moduleEditor->getPTIncompatibilityCode();
3103 
3104 			if (err)
3105 			{
3106 				// remove save panel
3107 				// => the handler for the upcoming modal dialog
3108 				// will note from the absent save panel that saving will be
3109 				// done to the current document
3110 				if (savePanel)
3111 				{
3112 					delete savePanel;
3113 					savePanel = NULL;
3114 				}
3115 				buildMODSaveErrorWarning((pp_int32)err);
3116 				return;
3117 			}
3118 		}
3119 		saveCurrentModuleAsSelectedType();
3120 	}
3121 }
3122 
saveAs()3123 void Tracker::saveAs()
3124 {
3125 	switch (moduleEditor->getSaveType())
3126 	{
3127 		case ModuleEditor::ModSaveTypeMOD:
3128 			saveType(FileTypes::FileTypeSongMOD);
3129 			break;
3130 
3131 		case ModuleEditor::ModSaveTypeXM:
3132 			saveType(FileTypes::FileTypeSongXM);
3133 			break;
3134 
3135 		default:
3136 			ASSERT(false);
3137 	}
3138 }
3139 
handleSaveProceed()3140 void Tracker::handleSaveProceed()
3141 {
3142 	// if there is a save panel present we save with file name
3143 	// selection, otherwise we simply save the current document
3144 	// in the current format
3145 	if (savePanel != NULL)
3146 		saveTypeWithDialog(currentSaveFileType, fileSystemChangedListener);
3147 	else
3148 		saveCurrentModuleAsSelectedType();
3149 }
3150 
handleSaveCancel()3151 void Tracker::handleSaveCancel()
3152 {
3153 	delete savePanel;
3154 	savePanel = NULL;
3155 }
3156 
buildMODSaveErrorWarning(pp_int32 error)3157 void Tracker::buildMODSaveErrorWarning(pp_int32 error)
3158 {
3159 	static const char* warnings[] = {"Song contains more than 31 instruments\nSave anyway?",
3160 
3161 									 "Song uses linear frequencies\nSave anyway?",
3162 
3163 									 "Song contains incompatible samples \n\n"\
3164 									 "Check for the following conditions:\n"\
3165 									 "* No 16 bit samples                \n"\
3166 									 "* All samples are below 64kb      \n"\
3167 									 "* No ping-pong looping             \n"\
3168 									 "* Panning is set to 0x80 (middle)  \n"\
3169 									 "* Relative note is C-4 (number 0)  \n\nSave anyway?",
3170 
3171 									 "Song contains FT2-style instruments\n\n"\
3172 									 "Check for the following conditions:\n"\
3173 									 "* No envelopes                     \n"\
3174 									 "* No autovibrato                   \n"\
3175 									 "* Only one sample per instrument   \n\nSave anyway?",
3176 
3177 									 "Incompatible pattern data          \n\n"\
3178 									 "Check for the following conditions:\n"\
3179 									 "* Pattern length is exactly 64 rows\n"\
3180 									 "* Only notes between C-3 and B-5   \n"\
3181 									 "* Volume column is not used        \n"\
3182 									 "* Only effects between 0 and F     \n\nSave anyway?"};
3183 
3184 	if (dialog)
3185 		delete dialog;
3186 
3187 	if (responder)
3188 		delete responder;
3189 
3190 	responder = new SaveProceedHandler(*this);
3191 
3192 	dialog = new PPDialogBase(screen, responder, MESSAGEBOX_SAVEPROCEED, "");
3193 
3194 	showMessageBoxSized(MESSAGEBOX_SAVEPROCEED, warnings[error-1], MessageBox_YESNO, 318, -1, false);
3195 
3196 	// Transfer ownership of modal overlayed container to dialog
3197 	// => also set new event listener
3198 	dialog->setMessageBoxContainer(messageBoxContainerGeneric);
3199 	messageBoxContainerGeneric->setEventListener(dialog);
3200 	PPSimpleVector<PPControl>& controls = messageBoxContainerGeneric->getControls();
3201 	for (pp_int32 i = 0; i < controls.size(); i++)
3202 		controls.get(i)->setEventListener(dialog);
3203 
3204 	messageBoxContainerGeneric = NULL;
3205 
3206 	dialog->show();
3207 }
3208 
estimateSongLength(bool signalWait)3209 void Tracker::estimateSongLength(bool signalWait/* = false*/)
3210 {
3211 	if (signalWait)
3212 		signalWaitState(true);
3213 
3214 	moduleEditor->getModuleServices()->estimateSongLength();
3215 
3216 	if (signalWait)
3217 	{
3218 		signalWaitState(false);
3219 		screen->update();
3220 	}
3221 }
3222 
signalWaitState(bool b)3223 void Tracker::signalWaitState(bool b)
3224 {
3225 	screen->signalWaitState(b, TrackerConfig::colorThemeMain);
3226 }
3227 
3228