1 /* vim:nowrap
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 
21 #include "CMainWindow.h"
22 
23 #include <stdexcept>
24 #include <algorithm>
25 #include <map>
26 #include <string>
27 #include <stdint.h>
28 
29 #include <CPath.h>
30 
31 #include "CActionMenuCommand.h"
32 
33 #include "CSoundFileManager.h"
34 
35 #include <CNestedDataFile/CNestedDataFile.h>
36 #include "settings.h"
37 
38 #include "CFOXIcons.h"
39 
40 #include "CAboutDialog.h"
41 
42 #include "../backend/main_controls.h"
43 
44 #include "../backend/CLoadedSound.h"
45 #include "../backend/AAction.h"
46 #include "../backend/ASoundClipboard.h"
47 
48 #include "../backend/CSoundPlayerChannel.h"
49 
50 #include "../backend/File/COpenAudioFileAction.h"
51 
52 #include "CSoundWindow.h"
53 
54 #include "CMetersWindow.h"
55 
56 #include "CUserNotesDialog.h"
57 #include "CCrossfadeEdgesDialog.h"
58 
59 #include "CKeyBindingsDialog.h"
60 
61 #include "rememberShow.h"
62 
63 #define RECORDING_MACRO_TIMER_INTERVAL 1000
64 
65 extern void enableAutoRepeat(void *display,bool enable); // QQQ defined in main.cpp
66 
67 /* TODO:
68  * 	- it is necesary for the owner to specifically delete the FXMenuPane objects it creates
69  */
70 
71 FXDEFMAP(CMainWindow) CMainWindowMap[]=
72 {
73 	//Message_Type				ID							Message_Handler
74 	FXMAPFUNC(SEL_CLOSE,			0,							CMainWindow::onQuit),
75 
76 		// file actions
77 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_REOPEN_FILE,				CMainWindow::onFileAction),
78 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_CLOSE_FILE,				CMainWindow::onFileAction),
79 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_REVERT_FILE,				CMainWindow::onFileAction),
80 
81 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_RECORD_MACRO,				CMainWindow::onFileAction),
82 	FXMAPFUNC(SEL_TIMEOUT,			CMainWindow::ID_RECORDING_MACRO_TIMER,			CMainWindow::onRecordingMacroTimer),
83 
84 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_EDIT_USERNOTES,				CMainWindow::onFileAction),
85 
86 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHOW_ABOUT,				CMainWindow::onFileAction),
87 
88 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SETUP_KEY_BINDINGS,			CMainWindow::onSetupKeyBindings),
89 
90 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_QUIT,					CMainWindow::onQuit),
91 
92 
93 		// play/record/transport controls
94 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_RECORD,					CMainWindow::onControlAction),
95 
96 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_ALL_ONCE,				CMainWindow::onControlAction),
97 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_ALL_LOOPED,			CMainWindow::onControlAction),
98 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_SELECTION_ONCE,			CMainWindow::onControlAction),
99 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_SELECTION_START_TO_END,		CMainWindow::onControlAction),
100 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_SELECTION_LOOPED,			CMainWindow::onControlAction),
101 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_SELECTION_LOOPED_SKIP_MOST,	CMainWindow::onControlAction),
102 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_SELECTION_LOOPED_GAP_BEFORE_REPEAT,CMainWindow::onControlAction),
103 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PLAY_LEFT_EDGE_OF_SCREEN_TO_END,	CMainWindow::onControlAction),
104 
105 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_STOP,					CMainWindow::onControlAction),
106 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PAUSE,					CMainWindow::onControlAction),
107 
108 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_JUMP_TO_BEGINNING,			CMainWindow::onControlAction),
109 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_JUMP_TO_SELECTION_START,		CMainWindow::onControlAction),
110 
111 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_JUMP_TO_NEXT_CUE,			CMainWindow::onControlAction),
112 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_JUMP_TO_PREV_CUE,			CMainWindow::onControlAction),
113 
114 	FXMAPFUNC(SEL_LEFTBUTTONRELEASE,	CMainWindow::ID_SHUTTLE_DIAL,				CMainWindow::onShuttleReturn),
115 	FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,	CMainWindow::ID_SHUTTLE_DIAL,				CMainWindow::onShuttleReturn),
116 	FXMAPFUNC(SEL_CHANGED,			CMainWindow::ID_SHUTTLE_DIAL,				CMainWindow::onShuttleChange),
117 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHUTTLE_DIAL_SPRING_BUTTON,		CMainWindow::onShuttleDialSpringButton),
118 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHUTTLE_DIAL_SCALE_BUTTON,		CMainWindow::onShuttleDialScaleButton),
119 
120 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHUTTLE_RETURN,				CMainWindow::onShuttleReturn),
121 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHUTTLE_BACKWARD,			CMainWindow::onKeyboardShuttle),
122 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHUTTLE_INCREASE_RATE,			CMainWindow::onKeyboardShuttle),
123 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SHUTTLE_FORWARD,			CMainWindow::onKeyboardShuttle),
124 
125 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_FIND_SELECTION_START,			CMainWindow::onControlAction),
126 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_FIND_SELECTION_STOP,			CMainWindow::onControlAction),
127 
128 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_ZOOM_IN,				CMainWindow::onControlAction),
129 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_ZOOM_FIT_SELECTION,			CMainWindow::onControlAction),
130 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_ZOOM_OUT,				CMainWindow::onControlAction),
131 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_ZOOM_OUT_FULL,				CMainWindow::onControlAction),
132 
133 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_TOGGLE_LEVEL_METERS,			CMainWindow::onControlAction),
134 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_TOGGLE_STEREO_PHASE_METERS,		CMainWindow::onControlAction),
135 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_TOGGLE_FREQUENCY_ANALYZER,		CMainWindow::onControlAction),
136 
137 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_UNDO_EDIT,				CMainWindow::onControlAction),
138 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_CLEAR_UNDO_HISTORY,			CMainWindow::onControlAction),
139 
140 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_REDRAW,					CMainWindow::onControlAction),
141 
142 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_DEFRAG_MENUITEM,			CMainWindow::onDebugButton),
143 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_PRINT_SAT_MENUITEM,			CMainWindow::onDebugButton),
144 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_VERIFY_SAT_MENUITEM,			CMainWindow::onDebugButton),
145 
146 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_FOLLOW_PLAY_POSITION_TOGGLE,		CMainWindow::onFollowPlayPositionButton),
147 
148 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_RENDER_CLIPPING_WARNING_TOGGLE,		CMainWindow::onRenderClippingWarningButton),
149 
150 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_DRAW_VERTICAL_CUE_POSITIONS_TOGGLE,	CMainWindow::onDrawVerticalCuePositionsButton),
151 
152 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_CROSSFADE_EDGES_COMBOBOX,		CMainWindow::onCrossfadeEdgesComboBox),
153 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_CROSSFADE_EDGES_SETTINGS_BUTTON,	CMainWindow::onCrossfadeEdgesSettings),
154 
155 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_CLIPBOARD_COMBOBOX,			CMainWindow::onClipboardComboBox),
156 
157 	FXMAPFUNC(SEL_CHANGED,			CMainWindow::ID_SOUND_LIST,				CMainWindow::onSoundListChange),
158 
159 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY_PREV,			CMainWindow::onSoundListHotKey),
160 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY1,			CMainWindow::onSoundListHotKey),
161 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY2,			CMainWindow::onSoundListHotKey),
162 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY3,			CMainWindow::onSoundListHotKey),
163 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY4,			CMainWindow::onSoundListHotKey),
164 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY5,			CMainWindow::onSoundListHotKey),
165 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY6,			CMainWindow::onSoundListHotKey),
166 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY7,			CMainWindow::onSoundListHotKey),
167 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY8,			CMainWindow::onSoundListHotKey),
168 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY9,			CMainWindow::onSoundListHotKey),
169 	FXMAPFUNC(SEL_COMMAND,			CMainWindow::ID_SOUND_LIST_HOTKEY10,			CMainWindow::onSoundListHotKey),
170 
171 	FXMAPFUNC(SEL_KEYPRESS,			CMainWindow::ID_SOUND_LIST,				CMainWindow::onHotKeyFocusFixup),
172 	FXMAPFUNC(SEL_KEYPRESS,			CMainWindow::ID_CROSSFADE_EDGES_COMBOBOX,		CMainWindow::onHotKeyFocusFixup),
173 	FXMAPFUNC(SEL_KEYPRESS,			CMainWindow::ID_CLIPBOARD_COMBOBOX,			CMainWindow::onHotKeyFocusFixup),
174 
175 	FXMAPFUNC(SEL_KEYPRESS,			0,							CMainWindow::onKeyPress),
176 	FXMAPFUNC(SEL_KEYRELEASE,		0,							CMainWindow::onKeyRelease),
177 	FXMAPFUNC(SEL_ENTER,			0,							CMainWindow::onMouseEnter),
178 };
179 
FXIMPLEMENT(CMainWindow,FXMainWindow,CMainWindowMap,ARRAYNUMBER (CMainWindowMap))180 FXIMPLEMENT(CMainWindow,FXMainWindow,CMainWindowMap,ARRAYNUMBER(CMainWindowMap))
181 
182 #include "drawPortion.h" // for backgroundColor
183 
184 #include "custom_cursors.h"
185 
186 CMainWindow::CMainWindow(FXApp* a) :
187 	FXMainWindow(a,"ReZound",FOXIcons->icon_logo_32,FOXIcons->icon_logo_16,DECOR_ALL,10,20,799,600, 0,0,0,0, 0,0),
188 	shuttleFont(NULL),
189 	soundListFont(NULL),
190 	soundListHeaderFont(NULL),
191 
192 	playMouseCursor(NULL),
193 	addCueMouseCursor(NULL),
194 
195 	toggleLevelMetersMenuItem(NULL),
196 	toggleStereoPhaseMetersMenuItem(NULL),
197 	toggleFrequencyAnalyzerMenuItem(NULL)
198 {
199 					// I'm aware of these two memory leaks, but I'm not concerned
200 	playMouseCursor=new FXCursor(a,bytesToBits(playMouseCursorSource,16*16),bytesToBits(playMouseCursorMask,16*16),16,16,14,8);
201 	playMouseCursor->create();
202 	addCueMouseCursor=new FXCursor(a,bytesToBits(addCueMouseCursorSource,16*16),bytesToBits(addCueMouseCursorMask,16*16),16,16,8,14);
203 	addCueMouseCursor->create();
204 
205 
206 	FXFontDesc d;
207 
208 	menubar=new FXMenuBar(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X|FRAME_RAISED|FRAME_THICK,0,0,0,0, 0,0,0,0);
209 	dummymenu=new FXMenuPane(this);
210 
211 	contents=new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0, 1,0);
212 
213 	metersWindow=new CMetersWindow(contents);
214 
215 	FXPacker *s,*t,*u;
216 
217 	s=new FXHorizontalFrame(contents,LAYOUT_FILL_X|FRAME_RAISED|FRAME_THICK, 0,0,0,0, 0,0,0,0, 2,0);
218 
219 	#define BUTTON_STYLE FRAME_RAISED|LAYOUT_EXPLICIT
220 	// build play control buttons
221 	FXPacker *playControlsFrame=new FXPacker(new FXPacker(s,LAYOUT_FILL_Y,0,0,0,0, 4,4,2,2),LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH, 0,0,(32*5)+10/*+10 to make room for 'semitones'*/,0, 0,0,0,0, 0,0);
222 		#define PLAY_CONTROLS_BUTTON_STYLE BUTTON_STYLE
223 		new FXButton(playControlsFrame,FXString("\t")+_("Play All Once"),FOXIcons->play_all_once,this,ID_PLAY_ALL_ONCE,PLAY_CONTROLS_BUTTON_STYLE, 32,0,32,32);
224 		new FXButton(playControlsFrame,FXString("\t")+_("Play Selection Once"),FOXIcons->play_selection_once,this,ID_PLAY_SELECTION_ONCE,PLAY_CONTROLS_BUTTON_STYLE, 32+32,0,32,32);
225 		new FXButton(playControlsFrame,FXString("\t")+_("Play From Selection Start to End"),FOXIcons->play_selection_start_to_end,this,ID_PLAY_SELECTION_START_TO_END,PLAY_CONTROLS_BUTTON_STYLE, 32+32+32,0,32,32);
226 		new FXButton(playControlsFrame,FXString("\t")+_("Play From Left Edge of Screen to End"),FOXIcons->play_screen,this,ID_PLAY_LEFT_EDGE_OF_SCREEN_TO_END,PLAY_CONTROLS_BUTTON_STYLE, 32+32+32+32,0,32,32);
227 		new FXButton(playControlsFrame,FXString("\t")+_("Play All Looped"),FOXIcons->play_all_looped,this,ID_PLAY_ALL_LOOPED,PLAY_CONTROLS_BUTTON_STYLE, 32,32,32,32);
228 		new FXButton(playControlsFrame,FXString("\t")+_("Play Selection Looped"),FOXIcons->play_selection_looped,this,ID_PLAY_SELECTION_LOOPED,PLAY_CONTROLS_BUTTON_STYLE, 32+32,32,32,32);
229 		new FXButton(playControlsFrame,FXString("\t")+_("Play Selection Looped but Skip Most of the Middle"),FOXIcons->play_selection_looped_skip_most,this,ID_PLAY_SELECTION_LOOPED_SKIP_MOST,PLAY_CONTROLS_BUTTON_STYLE, 32+32+32,32,32,32);
230 		new FXButton(playControlsFrame,FXString("\t")+_("Play Selection Looped and Play a Gap Before Repeating"),FOXIcons->play_selection_looped_gap_before_repeat,this,ID_PLAY_SELECTION_LOOPED_GAP_BEFORE_REPEAT,PLAY_CONTROLS_BUTTON_STYLE, 32+32+32+32,32,32,32);
231 
232 		new FXButton(playControlsFrame,FXString("\t")+_("Stop"),FOXIcons->stop,this,ID_STOP,PLAY_CONTROLS_BUTTON_STYLE, 0,0,32,32),
233 		new FXButton(playControlsFrame,FXString("\t")+_("Pause"),FOXIcons->pause,this,ID_PAUSE,PLAY_CONTROLS_BUTTON_STYLE, 0,32,32,32),
234 
235 		new FXButton(playControlsFrame,FXString("\t")+_("Record"),FOXIcons->record,this,ID_RECORD,PLAY_CONTROLS_BUTTON_STYLE, 32+32+32+32,32+32,32,32),
236 
237 		new FXButton(playControlsFrame,FXString("\t")+_("Jump to Beginning"),FOXIcons->jump_to_beginning,this,ID_JUMP_TO_BEGINNING,PLAY_CONTROLS_BUTTON_STYLE, 0,32+32,32+32,16);
238 		new FXButton(playControlsFrame,FXString("\t")+_("Jump to Start Position"),FOXIcons->jump_to_selection,this,ID_JUMP_TO_SELECTION_START,PLAY_CONTROLS_BUTTON_STYLE, 32+32,32+32,32+32,16);
239 
240 		new FXButton(playControlsFrame,FXString("\t")+_("Jump to Previous Cue"),FOXIcons->jump_to_previous_q,this,ID_JUMP_TO_PREV_CUE,PLAY_CONTROLS_BUTTON_STYLE, 0,32+32+16,32+32,16);
241 		new FXButton(playControlsFrame,FXString("\t")+_("Jump to Next Cue"),FOXIcons->jump_to_next_q,this,ID_JUMP_TO_NEXT_CUE,PLAY_CONTROLS_BUTTON_STYLE, 32+32,32+32+16,32+32,16);
242 
243 		// shuttle controls
244 		t=new FXHorizontalFrame(playControlsFrame,FRAME_NONE | LAYOUT_FIX_X|LAYOUT_FIX_Y, 0,32+32+16+16,0,0, 0,0,0,0, 0,0);
245 			u=new FXVerticalFrame(t,FRAME_NONE, 0,0,0,0, 0,0,0,0, 0,0);
246 
247 			shuttleFont=getApp()->getNormalFont();
248 			shuttleFont->getFontDesc(d);
249 			d.weight=FONTWEIGHT_LIGHT;
250 			d.size=65;
251 			shuttleFont=new FXFont(getApp(),d);
252 
253 			shuttleDial=new FXDial(u,this,ID_SHUTTLE_DIAL,DIAL_HORIZONTAL|DIAL_HAS_NOTCH|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_CENTER_Y, 0,0,32+32+32+32,20);
254 			shuttleDial->setRange(-((shuttleDial->getWidth())/2),(shuttleDial->getWidth())/2);
255 			shuttleDial->setRevolutionIncrement(shuttleDial->getWidth()*2-1);
256 			shuttleDial->setTipText(_("Shuttle Seek While Playing\n(right-click to return to middle)"));
257 
258 			shuttleLabel=new FXLabel(u,"1x",NULL,LAYOUT_FILL_X | JUSTIFY_CENTER_X, 0,0,0,0, 0,0,0,0);
259 			shuttleLabel->setFont(shuttleFont);
260 
261 			u=new FXVerticalFrame(t,FRAME_NONE,0,0,0,0, 0,0,0,0, 0,0);
262 				shuttleDialSpringButton=new FXToggleButton(u,_("free"),_("spring"),NULL,NULL,this,ID_SHUTTLE_DIAL_SPRING_BUTTON,LAYOUT_FILL_X|LAYOUT_SIDE_TOP | JUSTIFY_NORMAL | TOGGLEBUTTON_TOOLBAR | FRAME_RAISED,0,0,0,0, 1,1,0,0);
263 				shuttleDialSpringButton->setTipText(_("Set the Shuttle Wheel to Spring Back to the Middle or Not"));
264 				shuttleDialSpringButton->setState(true);
265 				shuttleDialSpringButton->setFont(shuttleFont);
266 
267 				shuttleDialScaleButton=new FXButton(u,FXString("100x\t")+_("Set the Maximum Rate Change of the Shuttle Wheel"),NULL,this,ID_SHUTTLE_DIAL_SCALE_BUTTON,LAYOUT_FILL_X|LAYOUT_SIDE_TOP | JUSTIFY_NORMAL | TOGGLEBUTTON_TOOLBAR | FRAME_RAISED,0,0,0,0, 1,1,0,0);
268 				shuttleDialScaleButton->setFont(shuttleFont);
269 
270 	new FXVerticalSeparator(s);
271 
272 	// build miscellaneous buttons
273 	FXPacker *miscControlsFrame=new FXPacker(new FXPacker(s,LAYOUT_FILL_Y,0,0,0,0, 4,4,2,2),LAYOUT_FILL_Y|LAYOUT_FILL_X, 0,0,0,0, 0,0,0,0, 3,2);
274 		//t=new FXHorizontalFrame(miscControlsFrame,0, 0,0,0,0, 0,0,0,0);
275 		followPlayPositionButton=new FXCheckButton(miscControlsFrame,_("Follow Play Position"),this,ID_FOLLOW_PLAY_POSITION_TOGGLE);
276 		followPlayPositionButton->setPadLeft(0); followPlayPositionButton->setPadRight(0); followPlayPositionButton->setPadTop(0); followPlayPositionButton->setPadBottom(0);
277 		renderClippingWarningButton=new FXCheckButton(miscControlsFrame,_("Clipping Warning"),this,ID_RENDER_CLIPPING_WARNING_TOGGLE);
278 		renderClippingWarningButton->setPadLeft(0); renderClippingWarningButton->setPadRight(0); renderClippingWarningButton->setPadTop(0); renderClippingWarningButton->setPadBottom(0);
279 		drawVerticalCuePositionsButton=new FXCheckButton(miscControlsFrame,_("Draw Vertical Cue Positions"),this,ID_DRAW_VERTICAL_CUE_POSITIONS_TOGGLE);
280 		drawVerticalCuePositionsButton->setPadLeft(0); drawVerticalCuePositionsButton->setPadRight(0); drawVerticalCuePositionsButton->setPadTop(0); drawVerticalCuePositionsButton->setPadBottom(0);
281 		t=new FXHorizontalFrame(miscControlsFrame,0, 0,0,0,0, 0,0,0,0);
282 			crossfadeEdgesComboBox=new FXComboBox(t,8,this,ID_CROSSFADE_EDGES_COMBOBOX, FRAME_SUNKEN|FRAME_THICK | COMBOBOX_NORMAL|COMBOBOX_STATIC | LAYOUT_CENTER_Y);
283 				crossfadeEdgesComboBox->setNumVisible(3);
284 				crossfadeEdgesComboBox->setTipText(_("After Most Actions a Crossfade can be Performed at the Start and Stop \nPositions to Give a Smoother Transition in to and out of the Modified Selection"));
285 				crossfadeEdgesComboBox->appendItem(_("No Crossfade"));
286 				crossfadeEdgesComboBox->appendItem(_("Crossfade Inner Edges"));
287 				crossfadeEdgesComboBox->appendItem(_("Crossfade Outer Edges"));
288 				crossfadeEdgesComboBox->setCurrentItem(0);
289 			new FXButton(t,FXString("...\t")+_("Change Crossfade Times"),NULL,this,ID_CROSSFADE_EDGES_SETTINGS_BUTTON, BUTTON_NORMAL & ~FRAME_THICK);
290 		clipboardComboBox=new FXComboBox(miscControlsFrame,8,this,ID_CLIPBOARD_COMBOBOX, FRAME_SUNKEN|FRAME_THICK | COMBOBOX_NORMAL|COMBOBOX_STATIC);
291 		clipboardComboBox->setNumVisible(8);
292 
293 			// have to 'create' both icons before using them
294 			FOXIcons->OffLED1->create();
295 			FOXIcons->RedLED1->create();
296 
297 		recordMacroButton=new FXButton(miscControlsFrame,(_("Record Macro")+string(" ")).c_str(),FOXIcons->OffLED1,this,ID_RECORD_MACRO, BUTTON_NORMAL|ICON_BEFORE_TEXT);
298 
299 
300 	new FXVerticalSeparator(s);
301 
302 	// build sound list
303 	t=new FXPacker(s,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 4,4,2,3, 0,0);
304 		t=new FXPacker(t,LAYOUT_FILL_X|LAYOUT_FILL_Y | FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0, 0,0);
305 			soundList=new FXIconList(t,this,ID_SOUND_LIST,HSCROLLER_NEVER|ICONLIST_BROWSESELECT|LAYOUT_FILL_X|LAYOUT_FILL_Y);
306 
307 				soundListFont=getApp()->getNormalFont();
308 				shuttleFont->getFontDesc(d);
309 				d.weight=FONTWEIGHT_NORMAL;
310 				d.size=80;
311 				soundListFont=new FXFont(getApp(),d);
312 
313 				soundList->setFont(soundListFont);
314 
315 				soundListHeaderFont=getApp()->getNormalFont();
316 				shuttleFont->getFontDesc(d);
317 				d.weight=FONTWEIGHT_BOLD;
318 				d.size=80;
319 				soundListHeaderFont=new FXFont(getApp(),d);
320 
321 				soundList->getHeader()->setFont(soundListHeaderFont);
322 				soundList->getHeader()->setPadLeft(2);
323 				soundList->getHeader()->setPadRight(2);
324 				soundList->getHeader()->setPadTop(0);
325 				soundList->getHeader()->setPadBottom(0);
326 
327 				soundList->appendHeader(" #",NULL,25);
328 				soundList->appendHeader("M",NULL,20);
329 				soundList->appendHeader(_("Name"),NULL,200);
330 				soundList->appendHeader(_("Path"),NULL,9999);
331 
332 	soundWindowFrame=new FXPacker(contents,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_RAISED|FRAME_THICK,0,0,0,0, 0,0,0,0, 0,0);
333 
334 	reopenActionFactory=new COpenAudioFileActionFactory(NULL);
335 }
336 
~CMainWindow()337 CMainWindow::~CMainWindow()
338 {
339 	delete playMouseCursor;
340 	delete addCueMouseCursor;
341 	delete shuttleFont;
342 	delete soundListFont;
343 	delete soundListHeaderFont;
344 	delete reopenActionFactory;
345 }
346 
show()347 void CMainWindow::show()
348 {
349 	FXint wantedWidth=getDefaultWidth();
350 	FXint wantedHeight=getDefaultHeight();
351 	rememberShow(this,"ReZound");
352 	resize(max(getWidth(),wantedWidth),max(getHeight(),wantedHeight));
353 
354 	FXMainWindow::show();
355 
356 	followPlayPositionButton->setCheck(gFollowPlayPosition);
357 
358 	renderClippingWarningButton->setCheck(gRenderClippingWarning);
359 
360 	drawVerticalCuePositionsButton->setCheck(gDrawVerticalCuePositions);
361 
362 	if(gCrossfadeEdges>=cetNone && gCrossfadeEdges<=cetOuter)
363 		crossfadeEdgesComboBox->setCurrentItem((FXint)gCrossfadeEdges);
364 	else
365 		crossfadeEdgesComboBox->setCurrentItem(0);
366 
367 
368 	// populate combo box to select clipboard
369 	for(size_t t=0;t<AAction::clipboards.size();t++)
370 		clipboardComboBox->appendItem(AAction::clipboards[t]->getDescription().c_str());
371 
372 	if(gWhichClipboard>=AAction::clipboards.size())
373 		gWhichClipboard=1;
374 
375 	clipboardComboBox->setCurrentItem(gWhichClipboard);
376 
377 #if REZ_FOX_VERSION>=10119
378 	dynamic_cast<FXMenuCheck *>(toggleLevelMetersMenuItem)->setCheck(gLevelMetersEnabled);
379 	dynamic_cast<FXMenuCheck *>(toggleStereoPhaseMetersMenuItem)->setCheck(gStereoPhaseMetersEnabled);
380 	dynamic_cast<FXMenuCheck *>(toggleFrequencyAnalyzerMenuItem)->setCheck(gFrequencyAnalyzerEnabled);
381 #else // older than 1.1.19 used FXMenuCommand
382 	if(gLevelMetersEnabled)
383 		toggleLevelMetersMenuItem->check();
384 	if(gStereoPhaseMetersEnabled)
385 		toggleStereoPhaseMetersMenuItem->check();
386 	if(gFrequencyAnalyzerEnabled)
387 		toggleFrequencyAnalyzerMenuItem->check();
388 #endif
389 
390 }
391 
hide()392 void CMainWindow::hide()
393 {
394 	rememberHide(this,"ReZound");
395 	FXMainWindow::hide();
396 }
397 
rebuildSoundWindowList()398 void CMainWindow::rebuildSoundWindowList()
399 {
400 	FXint xpos,ypos;
401 	soundList->getPosition(xpos,ypos);
402 
403 	soundList->clearItems();
404 	for(size_t t=0;t<gSoundFileManager->getOpenedCount();t++)
405 	{
406 		CSoundWindow *win=gSoundFileManager->getSoundWindow(t);
407 
408 		// add to sound window list
409 		CPath p(win->loadedSound->getFilename());
410 
411 		soundList->appendItem(
412 			(
413 			istring(t+1,2,false)+"\t"+
414 			(win->loadedSound->sound->isModified() ? "*" : "")+"\t"+
415 			p.baseName()+"\t"+
416 			p.dirName()
417 			).c_str(),
418 			NULL,NULL,win);
419 	}
420 
421 	soundList->forceRefresh();
422 
423 	soundList->setPosition(xpos,ypos); // scroll to the same position we were at before
424 
425 	// if it isn't visible make the active sound visible in the sound list
426 	CSoundWindow *active=gSoundFileManager->getActiveWindow();
427 	if(active!=NULL)
428 	{
429 		for(FXint t=0;t<soundList->getNumItems();t++)
430 		{
431 			if(soundList->getItemData(t)==(void *)active)
432 			{
433 				soundList->setCurrentItem(t);
434 				soundList->makeItemVisible(t);
435 				break;
436 			}
437 		}
438 	}
439 
440 }
441 
onSoundListChange(FXObject * sender,FXSelector sel,void * ptr)442 long CMainWindow::onSoundListChange(FXObject *sender,FXSelector sel,void *ptr)
443 {
444 	intptr_t index=(intptr_t)ptr;
445 
446 	if(index>=0 && index<soundList->getNumItems())
447 	{
448 		((CSoundWindow *)soundList->getItemData(index))->setActiveState(true);
449 
450 		if(AActionFactory::macroRecorder.isRecording())
451 		{
452 			Warning(_("You have changed the selected audio file while a macro is being recorded to position: ")+istring(index+1)+"\n\n"+_("Note that when the macro is played back the audio file loaded in the same position in the loaded sounds list will be selected at this step in the macro."));
453 			AActionFactory::macroRecorder.pushActiveSoundChange(index);
454 		}
455 	}
456 
457 
458 	return 1;
459 }
460 
461 extern CSoundWindow *previousActiveWindow;
onSoundListHotKey(FXObject * sender,FXSelector _sel,void * ptr)462 long CMainWindow::onSoundListHotKey(FXObject *sender,FXSelector _sel,void *ptr)
463 {
464 	FXEvent *ev=(FXEvent *)ptr;
465 	int sel=FXSELID(_sel);
466 
467 	if(sel==ID_SOUND_LIST_HOTKEY_PREV)
468 	{ // switch to previously active window
469 		if(previousActiveWindow!=NULL)
470 		{
471 			int index=0;
472 			for(;index<soundList->getNumItems();index++)
473 			{ // find the index in the sound list of the previous active window so we can set the current item
474 				if(((CSoundWindow *)soundList->getItemData(index))==previousActiveWindow)
475 					break;
476 			}
477 			soundList->setCurrentItem(index);
478 			soundList->makeItemVisible(soundList->getCurrentItem());
479 			return onSoundListChange(NULL,0,(void *)index);
480 		}
481 		return 1;
482 	}
483 	else
484 	{
485 		FXint index=0;
486 		switch(sel) // not assuming enum assigned continous increasing values (although it would probably be a safe assumption)
487 		{
488 			case ID_SOUND_LIST_HOTKEY1: index=0; break;
489 			case ID_SOUND_LIST_HOTKEY2: index=1; break;
490 			case ID_SOUND_LIST_HOTKEY3: index=2; break;
491 			case ID_SOUND_LIST_HOTKEY4: index=3; break;
492 			case ID_SOUND_LIST_HOTKEY5: index=4; break;
493 			case ID_SOUND_LIST_HOTKEY6: index=5; break;
494 			case ID_SOUND_LIST_HOTKEY7: index=6; break;
495 			case ID_SOUND_LIST_HOTKEY8: index=7; break;
496 			case ID_SOUND_LIST_HOTKEY9: index=8; break;
497 			case ID_SOUND_LIST_HOTKEY10: index=9; break;
498 	 		default: return 0;
499 		}
500 
501 		if(index<soundList->getNumItems())
502 		{
503 			soundList->setCurrentItem(index);
504 			soundList->makeItemVisible(soundList->getCurrentItem());
505 			return onSoundListChange(NULL,0,(void *)index);
506 		}
507 		else
508 			return 0;
509 	}
510 }
511 
512 /*
513 	This handler steals the key press events from the soundList FXIconList, clipboard
514 	FXComboBox, and the crossfade method FXComboBox because when focused they will take
515 	all my accelerator keys and search the handle them rather than pass them on to be
516 	handled by the accelerator table.  So, I steal all the keys except for keys like
517 	up, down, tab, et al.
518 
519 */
520 
onHotKeyFocusFixup(FXObject * sender,FXSelector sel,void * ptr)521 long CMainWindow::onHotKeyFocusFixup(FXObject *sender,FXSelector sel,void *ptr)
522 {
523 	switch(((FXEvent*)ptr)->code)
524 	{
525 	case KEY_Up:
526 	case KEY_KP_Up:
527 	case KEY_Down:
528 	case KEY_KP_Down:
529 
530 	case KEY_Page_Up:
531 	case KEY_KP_Page_Up:
532 	case KEY_Page_Down:
533 	case KEY_KP_Page_Down:
534 
535 	case KEY_Home:
536 	case KEY_KP_Home:
537 	case KEY_End:
538 	case KEY_KP_End:
539 
540 	case KEY_Tab:
541 	case KEY_KP_Tab:
542 		return 0;
543 	}
544 
545 	// kill the focus (so the generated event won't pass the event back to the
546 	// sender), then generate a key press event to the main window, then set
547 	// the focus again on the sender
548 	dynamic_cast<FXWindow *>(sender)->killFocus();
549 	this->handle(sender,FXSEL(SEL_KEYPRESS,0),ptr);
550 	static_cast<FXWindow *>(sender)->setFocus();
551 	return 1;
552 }
553 
554 
555 
556 
557 
558 
559 // --- stuff for handling that pressing ctrl should cause a play cursor to show on the wave canvas ---------------
560 
561 // goofy.. I don't like this, but it's the easiest thing I can think to do right now.. any other way seems to be graceless or rediculus
562 #include "FXWaveCanvas.h"
setMouseCursorForFXWaveCanvas(FXWindow * p,FXCursor * cursor)563 void setMouseCursorForFXWaveCanvas(FXWindow *p,FXCursor *cursor)
564 {
565 	if(dynamic_cast<FXWaveCanvas *>(p)!=NULL)
566 	{
567 		static_cast<FXWaveCanvas *>(p)->setDefaultCursor(cursor);
568 		static_cast<FXWaveCanvas *>(p)->setDragCursor(cursor);
569 	}
570 	else
571 	{
572 		for(int t=0;t<p->numChildren();t++)
573 			setMouseCursorForFXWaveCanvas(p->childAtIndex(t),cursor);
574 	}
575 }
576 
onKeyPress(FXObject * sender,FXSelector sel,void * ptr)577 long CMainWindow::onKeyPress(FXObject *sender,FXSelector sel,void *ptr)
578 {
579 	if(((FXEvent *)ptr)->code==KEY_Control_L || ((FXEvent *)ptr)->code==KEY_Control_R)
580 		// set play cursor
581 		setMouseCursorForFXWaveCanvas(soundWindowFrame,playMouseCursor);
582 	else if(((FXEvent *)ptr)->code==KEY_Shift_L || ((FXEvent *)ptr)->code==KEY_Shift_R)
583 		// set addCue cursor
584 		setMouseCursorForFXWaveCanvas(soundWindowFrame,addCueMouseCursor);
585 
586 	return FXMainWindow::handle(sender,sel,ptr); // behave as normal, just intercept ctrl presses
587 }
588 
onKeyRelease(FXObject * sender,FXSelector sel,void * ptr)589 long CMainWindow::onKeyRelease(FXObject *sender,FXSelector sel,void *ptr)
590 {
591 	if(((FXEvent *)ptr)->code==KEY_Control_L || ((FXEvent *)ptr)->code==KEY_Control_R)
592 		// unset play cursor
593 		setMouseCursorForFXWaveCanvas(soundWindowFrame,getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
594 	else if(((FXEvent *)ptr)->code==KEY_Shift_L || ((FXEvent *)ptr)->code==KEY_Shift_R)
595 		// unset addCue cursor
596 		setMouseCursorForFXWaveCanvas(soundWindowFrame,getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
597 
598 	return FXMainWindow::handle(sender,sel,ptr); // behave as normal, just intercept ctrl releases
599 }
600 
onMouseEnter(FXObject * sender,FXSelector sel,void * ptr)601 long CMainWindow::onMouseEnter(FXObject *sender,FXSelector sel,void *ptr)
602 {
603 	FXint dummy;
604 	FXuint keyboardModifierState;
605 	getCursorPosition(dummy,dummy,keyboardModifierState);
606 
607 	if(keyboardModifierState&CONTROLMASK)
608 		// set play cursor
609 		setMouseCursorForFXWaveCanvas(soundWindowFrame,playMouseCursor);
610 	else if(keyboardModifierState&SHIFTMASK)
611 		// set addCue cursor
612 		setMouseCursorForFXWaveCanvas(soundWindowFrame,addCueMouseCursor);
613 	else
614 		// unset custom cursor
615 		setMouseCursorForFXWaveCanvas(soundWindowFrame,getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
616 
617 	return 1;
618 }
619 
620 // ---------------------------------------------------------------------------------------------------------------
621 
622 
623 
624 
625 
626 
627 extern const string escapeAmpersand(const string i); // defined in CStatusComm.cpp
628 
629 /*
630    This is the class for the reopen submenu.  It intercepts calls to FXMenuPane::popup ()
631    so it can create the menu items which can change between each popup.
632 */
633 class CReopenPopup : public FXMenuPane
634 {
635 public:
CReopenPopup(FXWindow * owner)636 	CReopenPopup(FXWindow *owner) :
637 		FXMenuPane(owner)
638 	{
639 	}
640 
~CReopenPopup()641 	virtual ~CReopenPopup()
642 	{
643 	}
644 
popup(FXWindow * grabto,FXint x,FXint y,FXint w=0,FXint h=0)645 	virtual void popup(FXWindow* grabto, FXint x, FXint y, FXint w=0, FXint h=0)
646 	{
647 		// clear from previous popup
648 		// I can't do this on popdown because the event won't have happened yet needing the menu item's text for the filename)
649 		for(size_t t=0;t<items.size();t++)
650 			delete items[t];
651 		items.clear();
652 
653 		// create menu items
654 		size_t reopenSize=gSoundFileManager->getReopenHistorySize();
655 		if(reopenSize<=0)
656 			return;
657 		for(size_t t=0;t<reopenSize;t++)
658 		{
659 			FXMenuCommand *item=new FXMenuCommand(this,escapeAmpersand(gSoundFileManager->getReopenHistoryItem(t)).c_str(),NULL,getOwner(),CMainWindow::ID_REOPEN_FILE);
660 			item->create();
661 			items.push_back(item);
662 		}
663 
664 		FXMenuPane::popup(grabto,x,y,w,h);
665 	}
666 
667 	vector<FXMenuCommand *> items;
668 };
669 
670 /*
671    This is the class for the recent action submenu.  It intercepts calls to FXMenuPane::popup ()
672    so it can create the menu items which can change between each popup.
673 */
674 class CRecentActionsPopup : public FXMenuPane
675 {
676 public:
CRecentActionsPopup(FXWindow * owner)677 	CRecentActionsPopup(FXWindow *owner) :
678 		FXMenuPane(owner)
679 	{
680 		mainWindow=(CMainWindow *)owner;
681 	}
682 
~CRecentActionsPopup()683 	virtual ~CRecentActionsPopup()
684 	{
685 	}
686 
popup(FXWindow * grabto,FXint x,FXint y,FXint w=0,FXint h=0)687 	virtual void popup(FXWindow* grabto, FXint x, FXint y, FXint w=0, FXint h=0)
688 	{
689 		// clear from previous popup
690 		// I can't do this on popdown because the event won't have happened yet needing the menu item's text for the filename)
691 		for(size_t t=0;t<items.size();t++)
692 			delete items[t];
693 		items.clear();
694 
695 		// create menu items
696 		const vector<CActionMenuCommand *> &recentActions=mainWindow->recentActions;
697 		if(recentActions.size()<=0)
698 			return;
699 		for(size_t t=0;t<recentActions.size();t++)
700 		{
701 			CActionMenuCommand *item=new CActionMenuCommand(this,*recentActions[t]);
702 			item->create();
703 			items.push_back(item);
704 		}
705 
706 		FXMenuPane::popup(grabto,x,y,w,h);
707 	}
708 
709 	vector<CActionMenuCommand *> items;
710 	CMainWindow *mainWindow;
711 };
712 
actionMenuCommandTriggered(CActionMenuCommand * actionMenuCommand)713 void CMainWindow::actionMenuCommandTriggered(CActionMenuCommand *actionMenuCommand)
714 {
715 	for(vector<CActionMenuCommand *>::iterator i=recentActions.begin();i!=recentActions.end();i++)
716 	{
717 		if((*i)->getText()==actionMenuCommand->getText())
718 		{
719 			CActionMenuCommand *t=*i;
720 			recentActions.erase(i);
721 			recentActions.insert(recentActions.begin(),t);
722 			return;
723 		}
724 	}
725 
726 	if(recentActions.size()>=10)
727 		recentActions.pop_back();
728 	recentActions.insert(recentActions.begin(),actionMenuCommand);
729 }
730 
setWhichClipboard(size_t whichClipboard)731 void CMainWindow::setWhichClipboard(size_t whichClipboard)
732 {
733 	if(whichClipboard<AAction::clipboards.size())
734 	{
735 		clipboardComboBox->setCurrentItem(whichClipboard);
736 		gWhichClipboard=whichClipboard;
737 	}
738 }
739 
740 #include "CChannelSelectDialog.h"
741 #include "CPasteChannelsDialog.h"
742 
743 #include "../backend/CActionParameters.h"
744 
745 #include "../backend/CRunMacroAction.h"
746 
747 #include "../backend/File/FileActions.h"
748 #include "FileActionDialogs.h"
749 
750 #include "../backend/Edits/EditActions.h"
751 #include "../backend/Edits/CCueAction.h"
752 #include "EditActionDialogs.h"
753 
754 #include "../backend/Effects/EffectActions.h"
755 #include "EffectActionDialogs.h"
756 
757 #include "../backend/Filters/FilterActions.h"
758 #include "FilterActionDialogs.h"
759 
760 #include "../backend/Looping/LoopingActions.h"
761 #include "LoopingActionDialogs.h"
762 
763 #include "../backend/Remaster/RemasterActions.h"
764 #include "RemasterActionDialogs.h"
765 
766 #include "../backend/Generate/GenerateActions.h"
767 #include "GenerateActionDialogs.h"
768 
769 #include "../backend/LADSPA/LADSPAActions.h"
770 
771 
stripAmpersand(const string str)772 static const string stripAmpersand(const string str)
773 {
774 	string stripped;
775 	for(size_t t=0;t<str.length();t++)
776 		if(str[t]!='&') stripped+=str[t];
777 	return stripped;
778 }
779 
stripElipsis(const string str)780 static const string stripElipsis(const string str)
781 {
782 	if(str.rfind("...")==str.length()-3 && str.length()>3/*don't strip if that's the whole string*/)
783 		return str.substr(0,str.length()-3);
784 	return str;
785 }
786 
787 
788 /* the usual case -- adding a CActionMenuCommand to the menu item registry */
addToActionMap(CActionMenuCommand * item,map<const string,FXMenuCaption * > & menuItemRegistry)789 static void addToActionMap(CActionMenuCommand *item,map<const string,FXMenuCaption *> &menuItemRegistry)
790 {
791 		// I use the text right off the item because I don't want to pass a separate string to addToActionMap() just for the name
792 	const string strippedItemName=stripElipsis(stripAmpersand(item->getUntranslatedText()));
793 	if(menuItemRegistry.find(strippedItemName)!=menuItemRegistry.end()) // something a developer would want to know
794 		printf("NOTE: duplicate item name in menu item registry '%s'\n",strippedItemName.c_str());
795 	menuItemRegistry[strippedItemName]=item;
796 }
797 
798 /* the less usual case -- adding a general menu item to the menu item registry */
addToActionMap(const char * itemText,FXMenuCaption * item,map<const string,FXMenuCaption * > & menuItemRegistry)799 static void addToActionMap(const char *itemText,FXMenuCaption *item,map<const string,FXMenuCaption *> &menuItemRegistry)
800 { 	/* because I can't get the original text of the menu item with the hotkey in it, I pass it in separately and set the menuitem's text to it (prepended to what was already there).*/
801 
802 		// I use the text right off the item because I don't want to pass a separate string to addToActionMap() just for the name
803 	const string strippedItemName=stripElipsis(stripAmpersand(string(itemText)+item->getText().text()));
804 	if(menuItemRegistry.find(strippedItemName)!=menuItemRegistry.end()) // something a developer would want to know
805 		printf("NOTE: duplicate item name in menu item registry '%s'\n",strippedItemName.c_str());
806 
807 	if(itemText[0]) // not blank
808 		item->setText(gettext(itemText)+item->getText());
809 
810 	menuItemRegistry[strippedItemName]=item;
811 }
812 
813 // this registry is used to know what menu items exist that we might set key bindings for
814 map<string,FXMenuCommand *> gKeyBindingRegistry; // ??? could make this a datamemeber
815 
addActionToKeyBindingRegistery(const string name,FXMenuCommand * mc)816 void CMainWindow::addActionToKeyBindingRegistery(const string name,FXMenuCommand *mc)
817 {
818 	gKeyBindingRegistry[name]=mc;
819 }
820 
buildActionMap()821 void CMainWindow::buildActionMap()
822 {
823 	// This initializes the menu action map, creating the collection of all available menu actions.
824 	// This allows menus to be dynamically laid out using a configuration file.
825 
826 	// I know it's very wide, but it's (supposed to be) neat (tabs need to be set to 8 for it to look right)
827 
828 	// File
829 	addToActionMap(								new CActionMenuCommand(new CNewAudioFileActionFactory(new CNewAudioFileActionDialog(this)),dummymenu,							FOXIcons->file_new),													menuItemRegistry);
830 	addToActionMap(								new CActionMenuCommand(new COpenAudioFileActionFactory(new COpenAudioFileActionDialog(this)),dummymenu,							FOXIcons->file_open),													menuItemRegistry);
831 	addToActionMap(N_("&Reopen"),						new FXMenuCascade(dummymenu,														"",		FOXIcons->file_open,						new CReopenPopup(this)),				menuItemRegistry);
832 	addToActionMap(								new CActionMenuCommand(new CSaveAudioFileActionFactory,dummymenu,											FOXIcons->file_save),													menuItemRegistry);
833 	addToActionMap(								new CActionMenuCommand(new CSaveAsAudioFileActionFactory(new CSaveAsAudioFileActionDialog(this)),dummymenu,						FOXIcons->file_save_as),												menuItemRegistry);
834 	addToActionMap(								new CActionMenuCommand(new CSaveSelectionAsActionFactory(),dummymenu,											FOXIcons->file_save_as),												menuItemRegistry);
835 	addToActionMap(								new CActionMenuCommand(new CSaveAsMultipleFilesActionFactory(new CSaveAsMultipleFilesDialog(this)),dummymenu,						FOXIcons->file_save_as),												menuItemRegistry);
836 	addToActionMap(								new CActionMenuCommand(new CBurnToCDActionFactory(new CBurnToCDDialog(this)),dummymenu,									FOXIcons->file_burn),													menuItemRegistry);
837 		// not converting close or revert to AAction classes because they pull the sound object out from under the calling code in AAction
838 	addToActionMap(N_("&Close"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->file_close,						this,	ID_CLOSE_FILE),					menuItemRegistry);
839 	addToActionMap(N_("Re&vert"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->file_revert,						this,	ID_REVERT_FILE),				menuItemRegistry);
840 	// -
841 	addToActionMap(								new CActionMenuCommand(new CRunMacroActionFactory(new CRunMacroDialog(this)),dummymenu,									NULL),															menuItemRegistry);
842 	addToActionMap(N_("Record Macro..."),					new FXMenuCommand(dummymenu,														"",		FOXIcons->normal_action_buff,					this,	ID_RECORD_MACRO),				menuItemRegistry);
843 	// -
844 	addToActionMap(N_("User No&tes..."),					new FXMenuCommand(dummymenu,														"",		FOXIcons->notes,						this,	ID_EDIT_USERNOTES),				menuItemRegistry);
845 	// -
846 	addToActionMap(N_("&About ReZound"),					new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SHOW_ABOUT),					menuItemRegistry);
847 	addToActionMap(N_("&Setup Hotkeys..."),					new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SETUP_KEY_BINDINGS),				menuItemRegistry);
848 	// -
849 	addToActionMap("- Just for testing",					new FXMenuCaption(dummymenu,														""),																	menuItemRegistry);
850 	addToActionMap("Defrag",						new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_DEFRAG_MENUITEM),				menuItemRegistry);
851 	addToActionMap("PrintSAT",						new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_PRINT_SAT_MENUITEM),				menuItemRegistry);
852 	addToActionMap("VerifySAT",						new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_VERIFY_SAT_MENUITEM),			menuItemRegistry);
853 	// -
854 	addToActionMap(N_("&Quit"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->exit,							this,	ID_QUIT),					menuItemRegistry);
855 
856 
857 	// Control
858 	addToActionMap(N_("Record..."),						new FXMenuCommand(dummymenu,														"",		FOXIcons->small_record,						this,	ID_RECORD),					menuItemRegistry);
859 	addToActionMap(N_("Play All Once"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_all_once,					this,	ID_PLAY_ALL_ONCE),				menuItemRegistry);
860 	addToActionMap(N_("Play All Looped"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_all_looped,				this,	ID_PLAY_ALL_LOOPED),				menuItemRegistry);
861 	addToActionMap(N_("Play Selection Once"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_selection_once,				this,	ID_PLAY_SELECTION_ONCE),			menuItemRegistry);
862 	addToActionMap(N_("Play From Left Edge of Screen to End"),		new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_screen,	 				this,	ID_PLAY_LEFT_EDGE_OF_SCREEN_TO_END),		menuItemRegistry);
863 	addToActionMap(N_("Play From Selection Start to End"),			new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_selection_start_to_end,			this,	ID_PLAY_SELECTION_START_TO_END),		menuItemRegistry);
864 	addToActionMap(N_("Play Selection Looped"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_selection_looped,				this,	ID_PLAY_SELECTION_LOOPED),			menuItemRegistry);
865 	addToActionMap(N_("Loop Selection but Skip Most of the Middle"),	new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_selection_looped_skip_most,		this,	ID_PLAY_SELECTION_LOOPED_SKIP_MOST),		menuItemRegistry);
866 	addToActionMap(N_("Loop Selection and Play a Gap Before Repeating"),	new FXMenuCommand(dummymenu,														"",		FOXIcons->small_play_selection_looped_gap_before_repeat,	this,	ID_PLAY_SELECTION_LOOPED_GAP_BEFORE_REPEAT),	menuItemRegistry);
867 	addToActionMap(N_("Stop"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->small_stop,						this,	ID_STOP),					menuItemRegistry);
868 	addToActionMap(N_("Pause"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->small_pause,						this,	ID_PAUSE),					menuItemRegistry);
869 	// -
870 	addToActionMap(N_("Jump to Beginning"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->small_jump_to_beginning,				this,	ID_JUMP_TO_BEGINNING),				menuItemRegistry);
871 	addToActionMap(N_("Jump to Selection Start"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->small_jump_to_selection,				this,	ID_JUMP_TO_SELECTION_START),			menuItemRegistry);
872 	addToActionMap(N_("Jump to Previous Cue"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->small_jump_to_previous_q,				this,	ID_JUMP_TO_PREV_CUE),				menuItemRegistry);
873 	addToActionMap(N_("Jump to Next Cue"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->small_jump_to_next_q,					this,	ID_JUMP_TO_NEXT_CUE),				menuItemRegistry);
874 	// -
875 	addToActionMap(N_("Shuttle Rewind"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->shuttle_backward,					this,	ID_SHUTTLE_BACKWARD),				menuItemRegistry);
876 	addToActionMap(N_("Shuttle Amount"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->shuttle_normal,					this,	ID_SHUTTLE_INCREASE_RATE),			menuItemRegistry);
877 	addToActionMap(N_("Shuttle Forward"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->shuttle_forward,					this,	ID_SHUTTLE_FORWARD),				menuItemRegistry);
878 
879 
880 	// View Controls
881 	addToActionMap(N_("Zoom Out F&ull"),					new FXMenuCommand(dummymenu,														"",		FOXIcons->zoom_out_full,					this,	ID_ZOOM_OUT_FULL),				menuItemRegistry);
882 	addToActionMap(N_("Zoom &Out"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->zoom_out,						this,	ID_ZOOM_OUT),					menuItemRegistry);
883 	addToActionMap(N_("Zoom &In"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->zoom_in,						this,	ID_ZOOM_IN),					menuItemRegistry);
884 	addToActionMap(N_("Zoom &Fit Selection"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->zoom_fit,						this,	ID_ZOOM_FIT_SELECTION),				menuItemRegistry);
885 	// -
886 	addToActionMap(N_("Find &Start Position"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->normal_action_buff,					this,	ID_FIND_SELECTION_START),			menuItemRegistry);
887 	addToActionMap(N_("Find Sto&p Position"),				new FXMenuCommand(dummymenu,														"",		FOXIcons->normal_action_buff,					this,	ID_FIND_SELECTION_STOP),			menuItemRegistry);
888 	// -
889 	addToActionMap(N_("&Redraw"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->normal_action_buff,					this,	ID_REDRAW),					menuItemRegistry);
890 	// -
891 #if REZ_FOX_VERSION>=10119
892 	addToActionMap(N_("Toggle &Level Meters"),				toggleLevelMetersMenuItem=new FXMenuCheck(dummymenu,											"",										this,	ID_TOGGLE_LEVEL_METERS),			menuItemRegistry);
893 	addToActionMap(N_("Toggle &Stereo Phase Meters"),			toggleStereoPhaseMetersMenuItem=new FXMenuCheck(dummymenu,										"",										this,	ID_TOGGLE_STEREO_PHASE_METERS),			menuItemRegistry);
894 	addToActionMap(N_("Toggle Frequency &Analyzer"),			toggleFrequencyAnalyzerMenuItem=new FXMenuCheck(dummymenu,										"",										this,	ID_TOGGLE_FREQUENCY_ANALYZER),			menuItemRegistry);
895 #else // older than 1.1.19 used FXMenuCommand
896 	addToActionMap(N_("Toggle &Level Meters"),				toggleLevelMetersMenuItem=new FXMenuCommand(dummymenu,											"",		NULL,								this,	ID_TOGGLE_LEVEL_METERS),			menuItemRegistry);
897 	addToActionMap(N_("Toggle &Stereo Phase Meters"),			toggleStereoPhaseMetersMenuItem=new FXMenuCommand(dummymenu,										"",		NULL,								this,	ID_TOGGLE_STEREO_PHASE_METERS),			menuItemRegistry);
898 	addToActionMap(N_("Toggle Frequency &Analyzer"),			toggleFrequencyAnalyzerMenuItem=new FXMenuCommand(dummymenu,										"",		NULL,								this,	ID_TOGGLE_FREQUENCY_ANALYZER),			menuItemRegistry);
899 #endif
900 	// -
901 	addToActionMap(N_("Switch to Previously Viewed File"),			new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY_PREV),			menuItemRegistry);
902 	addToActionMap(N_("Switch to Loaded File 1"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY1),				menuItemRegistry);
903 	addToActionMap(N_("Switch to Loaded File 2"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY2),				menuItemRegistry);
904 	addToActionMap(N_("Switch to Loaded File 3"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY3),				menuItemRegistry);
905 	addToActionMap(N_("Switch to Loaded File 4"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY4),				menuItemRegistry);
906 	addToActionMap(N_("Switch to Loaded File 5"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY5),				menuItemRegistry);
907 	addToActionMap(N_("Switch to Loaded File 6"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY6),				menuItemRegistry);
908 	addToActionMap(N_("Switch to Loaded File 7"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY7),				menuItemRegistry);
909 	addToActionMap(N_("Switch to Loaded File 8"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY8),				menuItemRegistry);
910 	addToActionMap(N_("Switch to Loaded File 9"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY9),				menuItemRegistry);
911 	addToActionMap(N_("Switch to Loaded File 10"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_SOUND_LIST_HOTKEY10),			menuItemRegistry);
912 
913 
914 	// Edit
915 	addToActionMap(N_("Undo"),						new FXMenuCommand(dummymenu,														"",		FOXIcons->edit_undo,						this,	ID_UNDO_EDIT),					menuItemRegistry);
916 	addToActionMap(N_("Clear Undo History"),				new FXMenuCommand(dummymenu,														"",		NULL,								this,	ID_CLEAR_UNDO_HISTORY),				menuItemRegistry);
917 	// -
918 	addToActionMap(N_("&Recent Actions"),					new FXMenuCascade(dummymenu,														"",		NULL,								new CRecentActionsPopup(this)),				menuItemRegistry);
919 	// -
920 	addToActionMap(								new CActionMenuCommand(new CCopyEditFactory(gChannelSelectDialog),dummymenu,										FOXIcons->edit_copy),													menuItemRegistry);
921 	addToActionMap(								new CActionMenuCommand(new CCopyToNewEditFactory(gChannelSelectDialog),dummymenu,									FOXIcons->edit_copy),													menuItemRegistry);
922 	addToActionMap(								new CActionMenuCommand(new CCutEditFactory(gChannelSelectDialog),dummymenu,										FOXIcons->edit_cut),													menuItemRegistry);
923 	addToActionMap(								new CActionMenuCommand(new CCutToNewEditFactory(gChannelSelectDialog),dummymenu,									FOXIcons->edit_cut),													menuItemRegistry);
924 	addToActionMap(								new CActionMenuCommand(new CDeleteEditFactory(gChannelSelectDialog),dummymenu,										FOXIcons->edit_delete),													menuItemRegistry);
925 	addToActionMap(								new CActionMenuCommand(new CCropEditFactory(gChannelSelectDialog),dummymenu,										FOXIcons->edit_crop),													menuItemRegistry);
926 	// -
927 	addToActionMap(								new CActionMenuCommand(new CInsertPasteEditFactory(gPasteChannelsDialog),dummymenu,									FOXIcons->edit_paste),													menuItemRegistry);
928 	addToActionMap(								new CActionMenuCommand(new CReplacePasteEditFactory(gPasteChannelsDialog),dummymenu,									FOXIcons->edit_paste),													menuItemRegistry);
929 	addToActionMap(								new CActionMenuCommand(new COverwritePasteEditFactory(gPasteChannelsDialog),dummymenu,									FOXIcons->edit_paste),													menuItemRegistry);
930 	addToActionMap(								new CActionMenuCommand(new CLimitedOverwritePasteEditFactory(gPasteChannelsDialog),dummymenu,								FOXIcons->edit_paste),													menuItemRegistry);
931 	addToActionMap(								new CActionMenuCommand(new CMixPasteEditFactory(gPasteChannelsDialog),dummymenu,									FOXIcons->edit_paste),													menuItemRegistry);
932 	addToActionMap(								new CActionMenuCommand(new CLimitedMixPasteEditFactory(gPasteChannelsDialog),dummymenu,									FOXIcons->edit_paste),													menuItemRegistry);
933 	addToActionMap(								new CActionMenuCommand(new CFitMixPasteEditFactory(gPasteChannelsDialog),dummymenu,									FOXIcons->edit_paste),													menuItemRegistry);
934 	addToActionMap(								new CActionMenuCommand(new CPasteAsNewEditFactory,dummymenu,												FOXIcons->edit_paste),													menuItemRegistry);
935 	// -
936 	addToActionMap(								new CActionMenuCommand(new CInsertSilenceEditFactory(gChannelSelectDialog,new CInsertSilenceDialog(this)),dummymenu,					NULL),															menuItemRegistry);
937 	addToActionMap(								new CActionMenuCommand(new CMuteEditFactory(gChannelSelectDialog),dummymenu,										NULL),															menuItemRegistry);
938 	// -
939 	addToActionMap(								new CActionMenuCommand(new CAddChannelsEditFactory(new CAddChannelsDialog(this)),dummymenu,								NULL),															menuItemRegistry);
940 	addToActionMap(								new CActionMenuCommand(new CDuplicateChannelEditFactory(new CDuplicateChannelDialog(this)),dummymenu,							NULL),															menuItemRegistry);
941 	addToActionMap(								new CActionMenuCommand(new CRemoveChannelsEditFactory(gChannelSelectDialog),dummymenu,									NULL),															menuItemRegistry);
942 	addToActionMap(								new CActionMenuCommand(new CSwapChannelsEditFactory(new CSwapChannelsDialog(this)),dummymenu,								NULL),															menuItemRegistry);
943 	// -
944 	addToActionMap(								new CActionMenuCommand(new CRotateLeftEditFactory(gChannelSelectDialog,new CRotateDialog(this)),dummymenu,						NULL),															menuItemRegistry);
945 	addToActionMap(								new CActionMenuCommand(new CRotateRightEditFactory(gChannelSelectDialog,new CRotateDialog(this)),dummymenu,						NULL),															menuItemRegistry);
946 	// -
947 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sSelectAll),dummymenu,											NULL),															menuItemRegistry);
948 	addToActionMap(								new CActionMenuCommand(new CGrowOrSlideSelectionEditFactory(new CGrowOrSlideSelectionDialog(this)),dummymenu,						NULL),															menuItemRegistry);
949 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sSelectToBeginning),dummymenu,										NULL),															menuItemRegistry);
950 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sSelectToEnd),dummymenu,										NULL),															menuItemRegistry);
951 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sFlopToBeginning),dummymenu,										NULL),															menuItemRegistry);
952 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sFlopToEnd),dummymenu,											NULL),															menuItemRegistry);
953 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sSelectToSelectStart),dummymenu,									NULL),															menuItemRegistry);
954 	addToActionMap(								new CActionMenuCommand(new CSelectionEditFactory(sSelectToSelectStop),dummymenu,									NULL),															menuItemRegistry);
955 	// -
956 	addToActionMap(								new CActionMenuCommand(new CAddCueWhilePlayingActionFactory,dummymenu,												NULL),															menuItemRegistry);
957 
958 
959 	// Effects
960 	addToActionMap(								new CActionMenuCommand(new CReverseEffectFactory(gChannelSelectDialog),dummymenu,									NULL),															menuItemRegistry);
961 	// -
962 	addToActionMap(								new CActionMenuCommand(new CChangeVolumeEffectFactory(gChannelSelectDialog,new CNormalVolumeChangeDialog(this)),dummymenu,				NULL),															menuItemRegistry);
963 	addToActionMap(								new CActionMenuCommand(new CSimpleGainEffectFactory(gChannelSelectDialog,new CNormalGainDialog(this)),dummymenu,					NULL),															menuItemRegistry);
964 	addToActionMap(								new CActionMenuCommand(new CCurvedGainEffectFactory(gChannelSelectDialog,new CAdvancedGainDialog(this)),dummymenu,					NULL),															menuItemRegistry);
965 	// -
966 	addToActionMap(								new CActionMenuCommand(new CSimpleChangeRateEffectFactory(gChannelSelectDialog,new CNormalRateChangeDialog(this)),dummymenu,				NULL),															menuItemRegistry);
967 	addToActionMap(								new CActionMenuCommand(new CCurvedChangeRateEffectFactory(gChannelSelectDialog,new CAdvancedRateChangeDialog(this)),dummymenu,				NULL),															menuItemRegistry);
968 	// -
969 	addToActionMap(								new CActionMenuCommand(new CFlangeEffectFactory(gChannelSelectDialog,new CFlangeDialog(this)),dummymenu,						NULL),															menuItemRegistry);
970 	addToActionMap(								new CActionMenuCommand(new CSimpleDelayEffectFactory(gChannelSelectDialog,new CSimpleDelayDialog(this)),dummymenu,					NULL),															menuItemRegistry);
971 	addToActionMap(								new CActionMenuCommand(new CQuantizeEffectFactory(gChannelSelectDialog,new CQuantizeDialog(this)),dummymenu,						NULL),															menuItemRegistry);
972 	addToActionMap(								new CActionMenuCommand(new CDistortionEffectFactory(gChannelSelectDialog,new CDistortionDialog(this)),dummymenu,					NULL),															menuItemRegistry);
973 	addToActionMap(								new CActionMenuCommand(new CVariedRepeatEffectFactory(gChannelSelectDialog,new CVariedRepeatDialog(this)),dummymenu,					NULL),															menuItemRegistry);
974 
975 	addToActionMap(								new CActionMenuCommand(new CTestEffectFactory(gChannelSelectDialog),dummymenu,										NULL),															menuItemRegistry);
976 
977 
978 	// Filter
979 	addToActionMap(								new CActionMenuCommand(new CConvolutionFilterFactory(gChannelSelectDialog,new CConvolutionFilterDialog(this)),dummymenu,				NULL),															menuItemRegistry);
980 	addToActionMap(								new CActionMenuCommand(new CArbitraryFIRFilterFactory(gChannelSelectDialog,new CArbitraryFIRFilterDialog(this)),dummymenu,				FOXIcons->filter_custom),												menuItemRegistry);
981 	addToActionMap(								new CActionMenuCommand(new CMorphingArbitraryFIRFilterFactory(gChannelSelectDialog,new CMorphingArbitraryFIRFilterDialog(this)),dummymenu,		FOXIcons->filter_custom),												menuItemRegistry);
982 	// -
983 	addToActionMap(								new CActionMenuCommand(new CSinglePoleLowpassFilterFactory(gChannelSelectDialog,new CSinglePoleLowpassFilterDialog(this)),dummymenu,			FOXIcons->filter_lowpass),												menuItemRegistry);
984 	addToActionMap(								new CActionMenuCommand(new CSinglePoleHighpassFilterFactory(gChannelSelectDialog,new CSinglePoleHighpassFilterDialog(this)),dummymenu,			FOXIcons->filter_highpass),												menuItemRegistry);
985 	addToActionMap(								new CActionMenuCommand(new CBandpassFilterFactory(gChannelSelectDialog,new CBandpassFilterDialog(this)),dummymenu,					FOXIcons->filter_bandpass),												menuItemRegistry);
986 	addToActionMap(								new CActionMenuCommand(new CNotchFilterFactory(gChannelSelectDialog,new CNotchFilterDialog(this)),dummymenu,						FOXIcons->filter_notch),												menuItemRegistry);
987 	// -
988 	addToActionMap(								new CActionMenuCommand(new CBiquadResLowpassFilterFactory(gChannelSelectDialog,new CBiquadResLowpassFilterDialog(this)),dummymenu,			FOXIcons->filter_lowpass),												menuItemRegistry);
989 	addToActionMap(								new CActionMenuCommand(new CBiquadResHighpassFilterFactory(gChannelSelectDialog,new CBiquadResHighpassFilterDialog(this)),dummymenu,			FOXIcons->filter_highpass),												menuItemRegistry);
990 	addToActionMap(								new CActionMenuCommand(new CBiquadResBandpassFilterFactory(gChannelSelectDialog,new CBiquadResBandpassFilterDialog(this)),dummymenu,			FOXIcons->filter_bandpass),												menuItemRegistry);
991 
992 
993 	// Looping
994 	addToActionMap(								new CActionMenuCommand(new CMakeSymetricActionFactory(gChannelSelectDialog),dummymenu,									NULL),															menuItemRegistry);
995 	// -
996 	addToActionMap(								new CActionMenuCommand(new CAddNCuesActionFactory(new CAddNCuesDialog(this)),dummymenu,									NULL),															menuItemRegistry);
997 	addToActionMap(								new CActionMenuCommand(new CAddTimedCuesActionFactory(new CAddTimedCuesDialog(this)),dummymenu,								NULL),															menuItemRegistry);
998 
999 
1000 	// Remaster
1001 	addToActionMap(								new CActionMenuCommand(new CSimpleBalanceActionFactory(NULL,new CSimpleBalanceActionDialog(this)),dummymenu,						NULL),															menuItemRegistry);
1002 	addToActionMap(								new CActionMenuCommand(new CCurvedBalanceActionFactory(NULL,new CCurvedBalanceActionDialog(this)),dummymenu,						NULL),															menuItemRegistry);
1003 	// -
1004 	addToActionMap(								new CActionMenuCommand(new CMonoizeActionFactory(NULL,new CMonoizeActionDialog(this)),dummymenu,							NULL),															menuItemRegistry);
1005 	// -
1006 	addToActionMap(								new CActionMenuCommand(new CNoiseGateActionFactory(gChannelSelectDialog,new CNoiseGateDialog(this)),dummymenu,						NULL),															menuItemRegistry);
1007 	addToActionMap(								new CActionMenuCommand(new CCompressorActionFactory(gChannelSelectDialog,new CCompressorDialog(this)),dummymenu,					NULL),															menuItemRegistry);
1008 	addToActionMap(								new CActionMenuCommand(new CNormalizeActionFactory(gChannelSelectDialog,new CNormalizeDialog(this)),dummymenu,						NULL),															menuItemRegistry);
1009 	addToActionMap(								new CActionMenuCommand(new CAdaptiveNormalizeActionFactory(gChannelSelectDialog,new CAdaptiveNormalizeDialog(this)),dummymenu,				NULL),															menuItemRegistry);
1010 	addToActionMap(								new CActionMenuCommand(new CMarkQuietAreasActionFactory(new CMarkQuietAreasDialog(this)),dummymenu,							NULL),															menuItemRegistry);
1011 	addToActionMap(								new CActionMenuCommand(new CShortenQuietAreasActionFactory(new CShortenQuietAreasDialog(this)),dummymenu,							NULL),															menuItemRegistry);
1012 	// -
1013 	addToActionMap(								new CActionMenuCommand(new CResampleActionFactory(gChannelSelectDialog,new CResampleDialog(this)),dummymenu,						NULL),															menuItemRegistry);
1014 	addToActionMap(								new CActionMenuCommand(new CChangePitchActionFactory(gChannelSelectDialog,new CChangePitchDialog(this)),dummymenu,					NULL),															menuItemRegistry);
1015 	addToActionMap(								new CActionMenuCommand(new CChangeTempoActionFactory(gChannelSelectDialog,new CChangeTempoDialog(this)),dummymenu,					NULL),															menuItemRegistry);
1016 	// -
1017 	addToActionMap(								new CActionMenuCommand(new CRemoveDCActionFactory(gChannelSelectDialog),dummymenu,									NULL),															menuItemRegistry);
1018 	addToActionMap(								new CActionMenuCommand(new CInvertPhaseActionFactory(gChannelSelectDialog),dummymenu,									NULL),															menuItemRegistry);
1019 	// -
1020 	addToActionMap(								new CActionMenuCommand(new CUnclipActionFactory(gChannelSelectDialog),dummymenu,									NULL),															menuItemRegistry);
1021 
1022 	// Generate
1023 	addToActionMap(								new CActionMenuCommand(new CGenerateNoiseActionFactory(gChannelSelectDialog,new CGenerateNoiseDialog(this)),dummymenu,					NULL),															menuItemRegistry);
1024 	addToActionMap(								new CActionMenuCommand(new CGenerateToneActionFactory(gChannelSelectDialog,new CGenerateToneDialog(this)),dummymenu,					NULL),															menuItemRegistry);
1025 
1026 
1027 
1028 	// These are here simply so that xgettext will add entries for these in the rezound.pot file
1029 	N_("&File");
1030 	N_("&Edit");
1031 	N_("Selection");
1032 	N_("&View");
1033 	N_("&Control");
1034 	N_("Effec&ts");
1035 	N_("&Looping");
1036 	N_("F&ilters");
1037 	N_("&Remaster");
1038 	N_("&Generate");
1039 
1040 	// create registry of all actions (for macros)
1041 	gRegisteredActionFactories.clear();
1042 	for(map<const string,FXMenuCaption *>::iterator i=menuItemRegistry.begin();i!=menuItemRegistry.end();i++)
1043 	{
1044 		if(dynamic_cast<CActionMenuCommand *>(i->second)!=NULL)
1045 		{
1046 			gRegisteredActionFactories[i->first]=((CActionMenuCommand *)i->second)->getActionFactory();
1047 		}
1048 	}
1049 
1050 	// set key bindings
1051 	for(map<const string,FXMenuCaption *>::iterator i=menuItemRegistry.begin();i!=menuItemRegistry.end();i++)
1052 	{
1053 		if(dynamic_cast<FXMenuCommand *>(i->second)!=NULL)
1054 			addActionToKeyBindingRegistery(i->first,(FXMenuCommand *)i->second);
1055 	}
1056 
1057 }
1058 
buildMenu(FXMenuPane * menu,const CNestedDataFile * menuLayoutFile,const string menuKey,const string itemName)1059 void CMainWindow::buildMenu(FXMenuPane *menu,const CNestedDataFile *menuLayoutFile,const string menuKey,const string itemName)
1060 {
1061 	if(itemName=="-")
1062 	{
1063 		if(menu) new FXMenuSeparator(menu);
1064 		return;
1065 	}
1066 
1067 	// if the item is a submenu item, recur for each item in it; otherwise, add as normal menu item
1068 	if(menuLayoutFile->keyExists(menuKey)==CNestedDataFile::ktScope)
1069 	{	// add as a submenu
1070 		FXMenuPane *submenu=NULL;
1071 
1072 		const size_t DOTCount=istring(menuKey).count(CNestedDataFile::delim);
1073 		if(DOTCount==0)
1074 		{	// we've been passed just the layout name
1075 			// this is the theorical place to create menubar, but for FOX's needs I have to create it in the constructor
1076 		}
1077 		else if(DOTCount==1)
1078 		{	// we've been passed a top-level pull-down menu name
1079 			submenu=new FXMenuPane(this);
1080 			new FXMenuTitle(menubar,_(itemName.c_str()),NULL,submenu);
1081 		}
1082 		else if(DOTCount>1)
1083 		{	// submenu of menu
1084 			submenu=new FXMenuPane(this);
1085 			new FXMenuCascade(menu,_(itemName.c_str()),NULL,submenu);
1086 		}
1087 
1088 		const string menuItemsKey=menuKey DOT "menuitems";
1089 		if(menuLayoutFile->keyExists(menuItemsKey)==CNestedDataFile::ktValue)
1090 		{
1091 			const vector<string> menuItems=menuLayoutFile->getValue<vector<string> >(menuItemsKey);
1092 			for(size_t t=0;t<menuItems.size();t++)
1093 			{
1094 				const string name=menuItems[t];
1095 				buildMenu(submenu,menuLayoutFile,menuKey DOT stripAmpersand(name),name);
1096 			}
1097 		} // else (if it's not an array) something may be screwed up in the layout definition
1098 	}
1099 	else
1100 	{	// add as normal item
1101 		const string strippedItemName=stripAmpersand(itemName);
1102 		if(menuItemRegistry.find(strippedItemName)!=menuItemRegistry.end())
1103 		{
1104 			if(menuItemRegistry[strippedItemName]->getParent()!=dummymenu) // just a check
1105 				printf("NOTE: registered menu item '%s' was mapped more than once in layout; only the last one will be effective\n",strippedItemName.c_str());
1106 			menuItemRegistry[strippedItemName]->reparent(menu);
1107 
1108 			// change menu name's caption if there is an alias defined: 'origMenuName="new menu name";' in the same scope as where menuitems is defined
1109 			if(menuLayoutFile->keyExists(menuKey)==CNestedDataFile::ktValue)
1110 			{
1111 				FXMenuCaption *menuItem=menuItemRegistry[strippedItemName];
1112 				const string alias=menuLayoutFile->getValue<string>(menuKey);
1113 				menuItem->setText(gettext(alias.c_str()));
1114 			}
1115 		}
1116 		else
1117 		{
1118 			if(menu)
1119 				new FXMenuCommand(menu,(itemName+" (unregistered)").c_str(),NULL,this,0);
1120 			else
1121 				// since menu is NULL, this is a top-level menu without a defined body
1122 				new FXMenuTitle(menubar,(itemName+" (no subitems defined)").c_str(),NULL,new FXMenuPane(this));
1123 		}
1124 	}
1125 }
1126 
1127 #include <CNestedDataFile/CNestedDataFile.h>
createMenus()1128 void CMainWindow::createMenus()
1129 {
1130 	buildActionMap();
1131 
1132 	// Try to find out which set of menu information we should load and which file contains the menu information.
1133 	// The default menu layout is 'default' in .../share/.../menu.dat
1134 
1135 	// ??? make this a global setting instead of a lookup here
1136 	//	NOTE: this would be the first frontend specific global setting (but this isn't the only lookup (showAbout() also does it))
1137 	string menuLayout;
1138         if(gSettingsRegistry->keyExists("MenuLayout"))
1139 		menuLayout=gSettingsRegistry->getValue<string>("MenuLayout");
1140 	else
1141 		menuLayout="default";
1142 
1143 	tryAgain:
1144 
1145 	string menuLayoutFilename;
1146 	if(menuLayout=="default")
1147 		menuLayoutFilename=gSysDataDirectory+CPath::dirDelim+"menu.dat";
1148 	else
1149 		menuLayoutFilename=gUserDataDirectory+CPath::dirDelim+"menu.dat";
1150 
1151 	const CNestedDataFile *menuLayoutFile=new CNestedDataFile(menuLayoutFilename);
1152 	try
1153 	{
1154 		if(	menuLayoutFile->keyExists(menuLayout)!=CNestedDataFile::ktScope ||
1155 			menuLayoutFile->keyExists(menuLayout DOT "menuitems")!=CNestedDataFile::ktValue)
1156 		{
1157 			Warning(menuLayout+".menuitems array does not exist in requested menu layout '"+ menuLayout+"' in '"+menuLayoutFilename+"'");
1158 			menuLayout="default"; // go around again and use the default menu layout
1159 			goto tryAgain;
1160 		}
1161 
1162 		buildMenu(NULL,menuLayoutFile,menuLayout,menuLayout);
1163 		delete menuLayoutFile;
1164 	}
1165 	catch(...)
1166 	{
1167 		delete menuLayoutFile;
1168 		throw;
1169 	}
1170 
1171 	// give feedback about actions that didn't get mapped to a menu on screen
1172 	for(map<const string,FXMenuCaption *>::iterator i=menuItemRegistry.begin();i!=menuItemRegistry.end();i++)
1173 	{
1174 		if(i->second->getParent()==dummymenu)
1175 			printf("NOTE: registered menu item '%s' was not mapped anywhere in '%s' in layout '%s'\n",i->first.c_str(),menuLayoutFilename.c_str(),menuLayout.c_str());
1176 	}
1177 
1178 
1179 	buildLADSPAMenus();
1180 
1181 	setupKeyBindings();
1182 
1183 	create(); // it is necessary to call create again which will call it for all new child windows
1184 }
1185 
buildLADSPAMenus()1186 void CMainWindow::buildLADSPAMenus()
1187 {
1188 #ifdef USE_LADSPA
1189 	// now stick the LADSPA menu in there if it needs to be
1190 	// ??? (with dynamic menus, maybe let the layout define WHERE the ladspa submenu goes, (except it might not always be compiled for ladspa, so we wouldn't want to add it to the map if USE_LADSPA wasn't defined)
1191 	FXMenuPane *menu=new FXMenuPane(this);
1192 	new FXMenuTitle(menubar,"L&ADSPA",NULL,menu);
1193 		const vector<CLADSPAActionFactory *> LADSPAActionFactories=getLADSPAActionFactories();
1194 		if(LADSPAActionFactories.size()<=0)
1195 		{
1196 			new FXMenuCaption(menu,_("No LADSPA Plugins Found"));
1197 			new FXMenuSeparator(menu);
1198 			new FXMenuCaption(menu,_("Like PATH, set LADSPA_PATH to point"));
1199 			new FXMenuCaption(menu,_("to a directory(s) containing LADSPA"));
1200 			new FXMenuCaption(menu,_("plugin .so file at least once.  Or"));
1201 			new FXMenuCaption(menu,_("edit the value in ~/.rezound/registry.dat"));
1202 		}
1203 		else
1204 		{
1205 			// register all the LADSPA action so they can be used in macros
1206 			for(size_t t=0;t<LADSPAActionFactories.size();t++)
1207 				gRegisteredActionFactories[LADSPAActionFactories[t]->getName()]=LADSPAActionFactories[t];
1208 
1209 
1210 			// determine number of FXMenuCaption will fit on the screen (msh -> menu screen height)
1211 			const FXuint msh=(getApp()->getRootWindow()->getHeight()/(7+getApp()->getNormalFont()->getFontHeight()))-1;
1212 
1213 			if(LADSPAActionFactories.size()>20)
1214 			{
1215 				// add a submenu grouped by manufacturer
1216 				map<const string,map<const string,CLADSPAActionFactory *> > makerGrouped;
1217 				for(size_t t=0;t<LADSPAActionFactories.size();t++)
1218 				{
1219 					const string maker= LADSPAActionFactories[t]->getDescriptor()->Maker;
1220 					makerGrouped[maker][LADSPAActionFactories[t]->getName()]=LADSPAActionFactories[t];
1221 				}
1222 
1223 				if(makerGrouped.size()>1)
1224 				{ // more than one maker
1225 					FXMenuPane *makerMenu=new FXMenuPane(this);
1226 					new FXMenuCascade(menu,"By Maker",NULL,makerMenu);
1227 
1228 					for(map<const string,map<const string,CLADSPAActionFactory *> >::iterator i=makerGrouped.begin();i!=makerGrouped.end();i++)
1229 					{
1230 #if REZ_FOX_VERSION<10142
1231 						FXMenuPane *submenu=new FXMenuPane(this);
1232 #else
1233 						// if menu will be vertically taller than the screen, then make the menu scrollable
1234 						FXMenuPane *submenu=i->second.size()>msh ? new FXScrollPane(this,msh) : new FXMenuPane(this);
1235 #endif
1236 						new FXMenuCascade(makerMenu,i->first.c_str(),NULL,submenu);
1237 
1238 						for(map<const string,CLADSPAActionFactory *>::iterator t=i->second.begin();t!=i->second.end();t++)
1239 						{
1240 							FXMenuCommand *mc=new CActionMenuCommand(t->second,submenu,NULL);
1241 							addActionToKeyBindingRegistery(t->second->getName(),mc);
1242 						}
1243 					}
1244 				}
1245 
1246 				new FXMenuSeparator(menu);
1247 
1248 
1249 
1250 				// group by the first letter
1251 				map<const char,map<const string,CLADSPAActionFactory *> > nameGrouped;
1252 				for(size_t t=0;t<LADSPAActionFactories.size();t++)
1253 				{
1254 					const char letter= *(istring(LADSPAActionFactories[t]->getName()).upper()).begin();
1255 					nameGrouped[letter][LADSPAActionFactories[t]->getName()]=LADSPAActionFactories[t];
1256 				}
1257 
1258 				for(map<const char,map<const string,CLADSPAActionFactory *> >::iterator i=nameGrouped.begin();i!=nameGrouped.end();i++)
1259 				{
1260 #if REZ_FOX_VERSION<10142
1261 					FXMenuPane *submenu=new FXMenuPane(this);
1262 #else
1263 					// if menu will be vertically taller than the screen, then make the menu scrollable
1264 					FXMenuPane *submenu=i->second.size()>msh ? new FXScrollPane(this,msh) : new FXMenuPane(this);
1265 #endif
1266 					new FXMenuCascade(menu,string(&(i->first),1).c_str(),NULL,submenu);
1267 
1268 					for(map<const string,CLADSPAActionFactory *>::iterator t=i->second.begin();t!=i->second.end();t++)
1269 					{
1270 						FXMenuCommand *mc=new CActionMenuCommand(t->second,submenu,NULL);
1271 						addActionToKeyBindingRegistery(t->second->getName(),mc);
1272 					}
1273 				}
1274 			}
1275 			else
1276 			{
1277 				for(size_t t=0;t<LADSPAActionFactories.size();t++)
1278 				{
1279 					FXMenuCommand *mc=new CActionMenuCommand(LADSPAActionFactories[t],menu,NULL);
1280 					addActionToKeyBindingRegistery(LADSPAActionFactories[t]->getName(),mc);
1281 				}
1282 			}
1283 		}
1284 #endif
1285 }
1286 
1287 /* this function sets a key-binding if one has been defined for the given FXMenuCommand */
setupKeyBindings()1288 void CMainWindow::setupKeyBindings()
1289 {
1290 
1291 	// clear existing accelerator table (not doing this now.. see below)
1292 	//delete getAccelTable();
1293 	//setAccelTable(new FXAccelTable());
1294 
1295 	// reassign all accelerators (would prefer to wipe the slate clean before doing this, but there's no good way to restore the alt-&letter menu key bindings after doing so)
1296 	for(map<string,FXMenuCommand *>::iterator i=gKeyBindingRegistry.begin(); i!=gKeyBindingRegistry.end(); i++)
1297 	{
1298 		const string name=i->first;
1299 		FXMenuCommand *mc=i->second;
1300 
1301 		mc->setAccelText(""); // clear any existing accel text
1302 
1303 		if(gKeyBindingsStore->keyExists(name)==CNestedDataFile::ktValue)
1304 		{
1305 			string value=gKeyBindingsStore->getValue<string>(name);
1306 			if(value=="")
1307 				continue; // no key bound to this action
1308 
1309 			FXHotKey acckey=parseAccel(value.c_str());
1310 			mc->setAccelText(value.c_str());
1311 
1312 			/* these shuttle controls have to be handle specially because they require key up events to return the shuttle to the middle */
1313 			if(i->first=="Shuttle Forward")
1314 				getAccelTable()->addAccel(acckey,this,FXSEL(SEL_COMMAND,ID_SHUTTLE_FORWARD),FXSEL(SEL_COMMAND,ID_SHUTTLE_RETURN));
1315 			else if(i->first=="Shuttle Rewind")
1316 				getAccelTable()->addAccel(acckey,this,FXSEL(SEL_COMMAND,ID_SHUTTLE_BACKWARD),FXSEL(SEL_COMMAND,ID_SHUTTLE_RETURN));
1317 			else
1318 				getAccelTable()->addAccel(acckey,mc,FXSEL(SEL_COMMAND,FXMenuCommand::ID_ACCEL));
1319 		}
1320 	}
1321 }
1322 
onQuit(FXObject * sender,FXSelector sel,void * ptr)1323 long CMainWindow::onQuit(FXObject *sender,FXSelector sel,void *ptr)
1324 {
1325 
1326 	if(getApp()->getModality()==MODAL_FOR_WINDOW)
1327 	{ // don't allow a quit if there is a modal window showing
1328 		gStatusComm->beep();
1329 		return 1;
1330 	}
1331 
1332 	if(exitReZound(gSoundFileManager))
1333 	{
1334 		enableAutoRepeat(getApp()->getDisplay(),true); // QQQ (didn't work just putting this after application->run() in main.cpp)
1335 
1336 		hide();
1337 		getApp()->exit(0);
1338 	}
1339 	return 1;
1340 }
1341 
onSetupKeyBindings(FXObject * sender,FXSelector sel,void * ptr)1342 long CMainWindow::onSetupKeyBindings(FXObject *sender,FXSelector sel,void *ptr)
1343 {
1344 	// create a list of the keys that are currently bound because we need to remove those from the current accel table before assigning the new key bindings
1345 	// this is done to avoid having to delete and re-create the accel table each time in setupKeyBindings() which destroys the alt-&letter key bindings that
1346 	// already exist there.
1347 	vector<FXHotKey> removeKeyBindingsList;
1348 	const vector<string> actionsWithKeyBindings=gKeyBindingsStore->getChildKeys("");
1349 	for(size_t t=0;t<actionsWithKeyBindings.size();t++)
1350 		removeKeyBindingsList.push_back(parseAccel(gKeyBindingsStore->getValue<string>(actionsWithKeyBindings[t]).c_str()));
1351 
1352 	if(gKeyBindingsDialog->showIt(gKeyBindingRegistry))
1353 	{
1354 		for(size_t t=0;t<removeKeyBindingsList.size();t++)
1355 			getAccelTable()->removeAccel(removeKeyBindingsList[t]);
1356 
1357 		setupKeyBindings();
1358 	}
1359 	return 1;
1360 }
1361 
onFollowPlayPositionButton(FXObject * sender,FXSelector sel,void * ptr)1362 long CMainWindow::onFollowPlayPositionButton(FXObject *sender,FXSelector sel,void *ptr)
1363 {
1364 	gFollowPlayPosition=followPlayPositionButton->getCheck();
1365 	return 1;
1366 }
1367 
onRenderClippingWarningButton(FXObject * sender,FXSelector sel,void * ptr)1368 long CMainWindow::onRenderClippingWarningButton(FXObject *sender,FXSelector sel,void *ptr)
1369 {
1370 	gRenderClippingWarning=renderClippingWarningButton->getCheck();
1371 	if(gSoundFileManager->getActiveWindow())
1372 		gSoundFileManager->getActiveWindow()->updateFromEdit();
1373 	return 1;
1374 }
1375 
onDrawVerticalCuePositionsButton(FXObject * sender,FXSelector sel,void * ptr)1376 long CMainWindow::onDrawVerticalCuePositionsButton(FXObject *sender,FXSelector sel,void *ptr)
1377 {
1378 	gDrawVerticalCuePositions=drawVerticalCuePositionsButton->getCheck();
1379 	if(gSoundFileManager->getActiveWindow())
1380 		gSoundFileManager->getActiveWindow()->updateFromEdit();
1381 	return 1;
1382 }
1383 
onCrossfadeEdgesComboBox(FXObject * sender,FXSelector sel,void * ptr)1384 long CMainWindow::onCrossfadeEdgesComboBox(FXObject *sender,FXSelector sel,void *ptr)
1385 {
1386 	gCrossfadeEdges=(CrossfadeEdgesTypes)crossfadeEdgesComboBox->getCurrentItem();
1387 	return 1;
1388 }
1389 
onCrossfadeEdgesSettings(FXObject * sender,FXSelector sel,void * ptr)1390 long CMainWindow::onCrossfadeEdgesSettings(FXObject *sender,FXSelector sel,void *ptr)
1391 {
1392 	gCrossfadeEdgesDialog->showIt();
1393 	return 1;
1394 }
1395 
1396 
onClipboardComboBox(FXObject * sender,FXSelector sel,void * ptr)1397 long CMainWindow::onClipboardComboBox(FXObject *sender,FXSelector sel,void *ptr)
1398 {
1399 	gWhichClipboard=clipboardComboBox->getCurrentItem();
1400 	return 1;
1401 }
1402 
1403 
1404 // file action events
onFileAction(FXObject * sender,FXSelector sel,void * ptr)1405 long CMainWindow::onFileAction(FXObject *sender,FXSelector sel,void *ptr)
1406 {
1407 	switch(FXSELID(sel))
1408 	{
1409 	case ID_REOPEN_FILE:
1410 	{
1411 		vector<string> filenames;
1412 		filenames.push_back(dynamic_cast<FXMenuCommand *>(sender)->getText().text());
1413 
1414 		CActionParameters actionParameters(gSoundFileManager);
1415 		actionParameters.setValue<vector<string> >("filenames",filenames);
1416 
1417 		reopenActionFactory->performAction(NULL,&actionParameters,false);
1418 		break;
1419 	}
1420 
1421 	case ID_CLOSE_FILE:
1422 		if(AActionFactory::macroRecorder.isRecording())
1423 		{
1424 			Message(_("Cannot close a file while recording a macro"));
1425 			return 1;
1426 		}
1427 		closeSound(gSoundFileManager);
1428 		break;
1429 
1430 	case ID_REVERT_FILE:
1431 		if(AActionFactory::macroRecorder.isRecording())
1432 		{
1433 			Message(_("Cannot revert a file while recording a macro"));
1434 			return 1;
1435 		}
1436 		revertSound(gSoundFileManager);
1437 		break;
1438 
1439 	case ID_RECORD_MACRO:
1440 		recordMacro();
1441 		if(AActionFactory::macroRecorder.isRecording())
1442 		{
1443 			recordMacroButton->setState(1);
1444 			recordMacroButton->setIcon(FOXIcons->RedLED1);
1445 			// start blinking LED timer
1446 			getApp()->addTimeout(this,CMainWindow::ID_RECORDING_MACRO_TIMER,RECORDING_MACRO_TIMER_INTERVAL);
1447 		}
1448 		else
1449 		{
1450 			recordMacroButton->setIcon(FOXIcons->OffLED1);
1451 			recordMacroButton->setState(0);
1452 		}
1453 		break;
1454 
1455 	case ID_SHOW_ABOUT:
1456 		gAboutDialog->execute(PLACEMENT_SCREEN);
1457 		break;
1458 
1459 
1460 	case ID_EDIT_USERNOTES:
1461 #warning ??? make this an action?
1462 		try
1463 		{
1464 			CLoadedSound *s=gSoundFileManager->getActive();
1465 			if(s!=NULL)
1466 				gUserNotesDialog->show(s,PLACEMENT_CURSOR);
1467 			else
1468 				getApp()->beep();
1469 		}
1470 		catch(exception &e)
1471 		{
1472 			Error(e.what());
1473 		}
1474 		break;
1475 
1476 	default:
1477 		throw runtime_error(string(__func__)+" -- unhandled file button selector");
1478 	}
1479 	return 1;
1480 }
1481 
onRecordingMacroTimer(FXObject * sender,FXSelector sel,void * ptr)1482 long CMainWindow::onRecordingMacroTimer(FXObject *sender,FXSelector sel,void *ptr)
1483 {
1484 	static int counter=0;
1485 	if(AActionFactory::macroRecorder.isRecording())
1486 	{
1487 		recordMacroButton->setIcon( (counter%2) ? FOXIcons->RedLED1 : FOXIcons->OffLED1);
1488 		counter++;
1489 
1490 		// schedule for the next timer
1491 		getApp()->addTimeout(this,CMainWindow::ID_RECORDING_MACRO_TIMER,RECORDING_MACRO_TIMER_INTERVAL);
1492 	}
1493 	else
1494 		counter=0;
1495 
1496 	return 1;
1497 }
1498 
1499 // play control events
onControlAction(FXObject * sender,FXSelector sel,void * ptr)1500 long CMainWindow::onControlAction(FXObject *sender,FXSelector sel,void *ptr)
1501 {
1502 	switch(FXSELID(sel))
1503 	{
1504 	case ID_PLAY_ALL_ONCE:
1505 		metersWindow->resetGrandMaxPeakLevels();
1506 		play(gSoundFileManager,CSoundPlayerChannel::ltLoopNone,false);
1507 		break;
1508 
1509 	case ID_PLAY_ALL_LOOPED:
1510 		metersWindow->resetGrandMaxPeakLevels();
1511 		play(gSoundFileManager,CSoundPlayerChannel::ltLoopNormal,false);
1512 		break;
1513 
1514 	case ID_PLAY_SELECTION_ONCE:
1515 		metersWindow->resetGrandMaxPeakLevels();
1516 		play(gSoundFileManager,CSoundPlayerChannel::ltLoopNone,true);
1517 		break;
1518 
1519 	case ID_PLAY_SELECTION_START_TO_END:
1520 		metersWindow->resetGrandMaxPeakLevels();
1521 		if(gSoundFileManager->getActive())
1522 			play(gSoundFileManager,gSoundFileManager->getActive()->channel->getStartPosition());
1523 		break;
1524 
1525 	case ID_PLAY_SELECTION_LOOPED:
1526 		metersWindow->resetGrandMaxPeakLevels();
1527 		play(gSoundFileManager,CSoundPlayerChannel::ltLoopNormal,true);
1528 		break;
1529 
1530 	case ID_PLAY_SELECTION_LOOPED_SKIP_MOST:
1531 		metersWindow->resetGrandMaxPeakLevels();
1532 		play(gSoundFileManager,CSoundPlayerChannel::ltLoopSkipMost,true);
1533 		break;
1534 
1535 	case ID_PLAY_SELECTION_LOOPED_GAP_BEFORE_REPEAT:
1536 		metersWindow->resetGrandMaxPeakLevels();
1537 		play(gSoundFileManager,CSoundPlayerChannel::ltLoopGapBeforeRepeat,true);
1538 		break;
1539 
1540 	case ID_PLAY_LEFT_EDGE_OF_SCREEN_TO_END:
1541 		if(gSoundFileManager->getActiveWindow())
1542 		{
1543 			metersWindow->resetGrandMaxPeakLevels();
1544 			play(gSoundFileManager,gSoundFileManager->getActiveWindow()->getLeftEdgePosition());
1545 		}
1546 		break;
1547 
1548 	case ID_STOP:
1549 		stop(gSoundFileManager);
1550 		break;
1551 
1552 	case ID_PAUSE:
1553 		pause(gSoundFileManager);
1554 		break;
1555 
1556 	case ID_RECORD:
1557 		if(AActionFactory::macroRecorder.isRecording())
1558 		{
1559 			Message(_("Cannot record audio while recording a macro"));
1560 			return 1;
1561 		}
1562 		recordSound(gSoundFileManager);
1563 		break;
1564 
1565 	case ID_JUMP_TO_BEGINNING:
1566 		jumpToBeginning(gSoundFileManager);
1567 		break;
1568 
1569 	case ID_JUMP_TO_SELECTION_START:
1570 		jumpToStartPosition(gSoundFileManager);
1571 		break;
1572 
1573 	case ID_JUMP_TO_PREV_CUE:
1574 		jumpToPreviousCue(gSoundFileManager);
1575 		break;
1576 
1577 	case ID_JUMP_TO_NEXT_CUE:
1578 		jumpToNextCue(gSoundFileManager);
1579 		break;
1580 
1581 
1582 	case ID_UNDO_EDIT:
1583 		undo(gSoundFileManager);
1584 		break;
1585 
1586 	case ID_CLEAR_UNDO_HISTORY:
1587 		clearUndoHistory(gSoundFileManager);
1588 		break;
1589 
1590 
1591 	case ID_FIND_SELECTION_START:
1592 		if(gSoundFileManager->getActiveWindow())
1593 			gSoundFileManager->getActiveWindow()->centerStartPos();
1594 		break;
1595 
1596 	case ID_FIND_SELECTION_STOP:
1597 		if(gSoundFileManager->getActiveWindow())
1598 			gSoundFileManager->getActiveWindow()->centerStopPos();
1599 		break;
1600 
1601 
1602 	case ID_TOGGLE_LEVEL_METERS:
1603 #if REZ_FOX_VERSION>=10119
1604 		metersWindow->enableLevelMeters(dynamic_cast<FXMenuCheck *>(sender)->getCheck());
1605 #else // older than 1.1.19 used FXMenuCommand
1606 		if(dynamic_cast<FXMenuCommand *>(sender)->isChecked())
1607 			dynamic_cast<FXMenuCommand *>(sender)->uncheck();
1608 		else
1609 			dynamic_cast<FXMenuCommand *>(sender)->check();
1610 		metersWindow->enableLevelMeters(dynamic_cast<FXMenuCommand *>(sender)->isChecked());
1611 #endif
1612 		break;
1613 
1614 	case ID_TOGGLE_STEREO_PHASE_METERS:
1615 #if REZ_FOX_VERSION>=10119
1616 		metersWindow->enableStereoPhaseMeters(dynamic_cast<FXMenuCheck *>(sender)->getCheck());
1617 #else // older than 1.1.19 used FXMenuCommand
1618 		if(dynamic_cast<FXMenuCommand *>(sender)->isChecked())
1619 			dynamic_cast<FXMenuCommand *>(sender)->uncheck();
1620 		else
1621 			dynamic_cast<FXMenuCommand *>(sender)->check();
1622 		metersWindow->enableStereoPhaseMeters(dynamic_cast<FXMenuCommand *>(sender)->isChecked());
1623 #endif
1624 		break;
1625 
1626 	case ID_TOGGLE_FREQUENCY_ANALYZER:
1627 #if REZ_FOX_VERSION>=10119
1628 		metersWindow->enableFrequencyAnalyzer(dynamic_cast<FXMenuCheck *>(sender)->getCheck());
1629 #else // older than 1.1.19 used FXMenuCommand
1630 		if(dynamic_cast<FXMenuCommand *>(sender)->isChecked())
1631 			dynamic_cast<FXMenuCommand *>(sender)->uncheck();
1632 		else
1633 			dynamic_cast<FXMenuCommand *>(sender)->check();
1634 		metersWindow->enableFrequencyAnalyzer(dynamic_cast<FXMenuCommand *>(sender)->isChecked());
1635 #endif
1636 		break;
1637 
1638 
1639 	case ID_ZOOM_IN:
1640 		if(gSoundFileManager->getActiveWindow())
1641 			gSoundFileManager->getActiveWindow()->horzZoomInSome();
1642 		break;
1643 
1644 	case ID_ZOOM_FIT_SELECTION:
1645 		if(gSoundFileManager->getActiveWindow())
1646 			gSoundFileManager->getActiveWindow()->horzZoomSelectionFit();
1647 		break;
1648 
1649 	case ID_ZOOM_OUT:
1650 		if(gSoundFileManager->getActiveWindow())
1651 			gSoundFileManager->getActiveWindow()->horzZoomOutSome();
1652 		break;
1653 
1654 	case ID_ZOOM_OUT_FULL:
1655 		if(gSoundFileManager->getActiveWindow())
1656 			gSoundFileManager->getActiveWindow()->horzZoomOutFull();
1657 		break;
1658 
1659 
1660 	case ID_REDRAW:
1661 		if(gSoundFileManager->getActiveWindow())
1662 			gSoundFileManager->getActiveWindow()->redraw();
1663 		break;
1664 
1665 	default:
1666 		throw runtime_error(string(__func__)+" -- unhandled play button selector");
1667 	}
1668 	return 1;
1669 }
1670 
onShuttleReturn(FXObject * sender,FXSelector sel,void * ptr)1671 long CMainWindow::onShuttleReturn(FXObject *sender,FXSelector sel,void *ptr)
1672 {
1673 	if(((FXEvent *)ptr)->code==LEFTBUTTON && !shuttleDialSpringButton->getState())
1674 		return 1; // this wasn't a left click release and where we're in spring-back mode
1675 
1676 	enableAutoRepeat(getApp()->getDisplay(),true); // QQQ
1677 
1678 	// return shuttle control to the middle
1679 	shuttleDial->setValue(0);
1680 	onShuttleChange(NULL,0,NULL);
1681 	return 1;
1682 }
1683 
onShuttleChange(FXObject * sender,FXSelector sel,void * ptr)1684 long CMainWindow::onShuttleChange(FXObject *sender,FXSelector sel,void *ptr)
1685 {
1686 	CSoundWindow *w=gSoundFileManager->getActiveWindow();
1687 	if(w!=NULL)
1688 	{
1689 		CLoadedSound *s=w->loadedSound;
1690 
1691 		string labelString;
1692 
1693 		FXint minValue,maxValue;
1694 		shuttleDial->getRange(minValue,maxValue);
1695 
1696 		const FXint shuttlePos=shuttleDial->getValue();
1697 		float seekSpeed;
1698 
1699 		const string text=shuttleDialScaleButton->getText().text();
1700 
1701 		if(shuttlePos==0)
1702 		{
1703 			if(text==_("semitones"))
1704 				labelString="+ 0";
1705 			else
1706 				labelString="1x";
1707 
1708 			seekSpeed=1.0;
1709 		}
1710 		else
1711 		{
1712 			if(text=="1x")
1713 			{ // 1x +/- (0..1]
1714 				if(shuttlePos>0)
1715 					seekSpeed=(double)shuttlePos/(double)maxValue;
1716 				else //if(shuttlePos<0)
1717 					seekSpeed=(double)-shuttlePos/(double)minValue;
1718 				labelString=istring(seekSpeed,4,3)+"x";
1719 			}
1720 			else if(text=="2x")
1721 			{ // 2x +/- [1..2]
1722 				if(shuttlePos>0)
1723 					seekSpeed=(double)shuttlePos/(double)maxValue+1.0;
1724 				else //if(shuttlePos<0)
1725 					seekSpeed=(double)-shuttlePos/(double)minValue-1.0;
1726 				labelString=istring(seekSpeed,3,2)+"x";
1727 			}
1728 			else if(text=="100x")
1729 			{ // 100x +/- [1..100]
1730 						// I square the value to give a more useful range
1731 				if(shuttlePos>0)
1732 					seekSpeed=(pow((double)shuttlePos/(double)maxValue,2.0)*99.0)+1.0;
1733 				else //if(shuttlePos<0)
1734 					seekSpeed=(pow((double)shuttlePos/(double)minValue,2.0)*-99.0)-1.0;
1735 				labelString=istring(seekSpeed,4,2)+"x";
1736 			}
1737 			else if(text==_("semitones"))
1738 			{ // semitone + [0.5..2]
1739 				float semitones = round((double)shuttlePos/(double)maxValue*12); // +/- 12 semitones
1740 				if(shuttlePos>0)
1741 				{
1742 					semitones = round((double)shuttlePos/(double)maxValue*12);
1743 					seekSpeed=pow(2.0,semitones/12.0);
1744 				}
1745 				else //if(shuttlePos<0)
1746 				{
1747 					semitones = round((double)shuttlePos/(double)minValue*12);
1748 					seekSpeed=pow(0.5,semitones/12.0);
1749 				}
1750 				labelString = (semitones>=0 ? "+" : "") + istring((int)semitones,2,false) + " ("+istring(seekSpeed,3,2)+"x)";
1751 			}
1752 			else
1753 				throw runtime_error(string(__func__)+" -- internal error -- unhandled text for shuttleDialScaleButton: '"+text+"'");
1754 		}
1755 
1756 		shuttleLabel->setText(labelString.c_str());
1757 		w->shuttleControlScalar=shuttleDialScaleButton->getText().text();
1758 		w->shuttleControlSpringBack=shuttleDialSpringButton->getState();
1759 		s->channel->setSeekSpeed(seekSpeed);
1760 	}
1761 
1762 	return 1;
1763 }
1764 
positionShuttleGivenSpeed(double seekSpeed,const string shuttleControlScalar,bool springBack)1765 void CMainWindow::positionShuttleGivenSpeed(double seekSpeed,const string shuttleControlScalar,bool springBack)
1766 {
1767 	FXint minValue,maxValue;
1768 	shuttleDial->getRange(minValue,maxValue);
1769 
1770 	FXint shuttlePos;
1771 	if(seekSpeed==1.0)
1772 		shuttlePos=0;
1773 	else
1774 	{
1775 		const string &text=shuttleControlScalar;
1776 		if(text=="1x")
1777 		{
1778 			if(seekSpeed>0.0)
1779 				shuttlePos=(FXint)(seekSpeed*maxValue);
1780 			else //if(seekSpeed<0.0)
1781 				shuttlePos=(FXint)(-seekSpeed*minValue);
1782 		}
1783 		else if(text=="2x")
1784 		{
1785 			if(seekSpeed>0.0)
1786 				shuttlePos=(FXint)((seekSpeed-1.0)*maxValue);
1787 			else //if(seekSpeed<0.0)
1788 				shuttlePos=(FXint)(-(seekSpeed+1.0)*minValue);
1789 		}
1790 		else if(text=="100x")
1791 		{
1792 			if(seekSpeed>0.0)
1793 				shuttlePos=(FXint)(maxValue*sqrt((seekSpeed-1.0)/100.0));
1794 			else //if(seekSpeed<0.0)
1795 				shuttlePos=(FXint)(minValue*sqrt((seekSpeed+1.0)/-100.0));
1796 		}
1797 		else if(text==_("semitones"))
1798 		{
1799 			if(seekSpeed>0.0)
1800 				shuttlePos=(FXint)(log(seekSpeed)/log(2.0)*maxValue);
1801 			else //if(seekSpeed<0.0)
1802 				shuttlePos=(FXint)(log(seekSpeed)/log(2.0)*minValue);
1803 		}
1804 		else
1805 			throw runtime_error(string(__func__)+" -- internal error -- unhandled text for shuttleDialScaleButton: '"+text+"'");
1806 
1807 	}
1808 
1809 	shuttleDialScaleButton->setText(shuttleControlScalar.c_str());
1810 	shuttleDialSpringButton->setState(springBack);
1811 	shuttleDial->setValue(shuttlePos);
1812 }
1813 
onShuttleDialSpringButton(FXObject * sender,FXSelector sel,void * ptr)1814 long CMainWindow::onShuttleDialSpringButton(FXObject *sender,FXSelector sel,void *ptr)
1815 {
1816 	shuttleDialSpringButton->killFocus();
1817 	if(shuttleDialSpringButton->getState())
1818 	{
1819 		// return the shuttle control to the middle
1820 		shuttleDial->setValue(0);
1821 		onShuttleChange(NULL,0,NULL);
1822 	}
1823 	return 1;
1824 }
1825 
onShuttleDialScaleButton(FXObject * sender,FXSelector sel,void * ptr)1826 long CMainWindow::onShuttleDialScaleButton(FXObject *sender,FXSelector sel,void *ptr)
1827 {
1828 	shuttleDialScaleButton->killFocus();
1829 	const string text=shuttleDialScaleButton->getText().text();
1830 	if(text=="100x")
1831 		shuttleDialScaleButton->setText("1x");
1832 	else if(text=="1x")
1833 		shuttleDialScaleButton->setText("2x");
1834 	else if(text=="2x")
1835 		shuttleDialScaleButton->setText(_("semitones"));
1836 	else if(text==_("semitones"))
1837 		shuttleDialScaleButton->setText("100x");
1838 	else
1839 		throw runtime_error(string(__func__)+" -- internal error -- unhandled text for shuttleDialScaleButton: '"+text+"'");
1840 
1841 	// return the shuttle control to the middle
1842 	shuttleDial->setValue(0);
1843 	onShuttleChange(NULL,0,NULL);
1844 
1845 	return 1;
1846 }
1847 
onKeyboardShuttle(FXObject * sender,FXSelector sel,void * ptr)1848 long CMainWindow::onKeyboardShuttle(FXObject *sender,FXSelector sel,void *ptr)
1849 {
1850 	FXint lo,hi;
1851 	shuttleDial->getRange(lo,hi);
1852 
1853 	FXint inc= (hi-lo)/14; // 7 positions surrounding 0
1854 
1855 	FXint pos=shuttleDial->getValue();
1856 
1857 	if(pos==0 && FXSELID(sel)==ID_SHUTTLE_BACKWARD)
1858 	{
1859 		if((uintptr_t)ptr > 1/*came from keyboard event*/) enableAutoRepeat(getApp()->getDisplay(),false); // QQQ
1860 		shuttleDial->setValue(pos-inc);
1861 		onShuttleChange(sender,sel,ptr);
1862 	}
1863 	else if(pos==0 && FXSELID(sel)==ID_SHUTTLE_FORWARD)
1864 	{
1865 		if((uintptr_t)ptr > 1/*came from keyboard event*/) enableAutoRepeat(getApp()->getDisplay(),false); // QQQ
1866 		shuttleDial->setValue(pos+inc);
1867 		onShuttleChange(sender,sel,ptr);
1868 	}
1869 	else if(pos!=0 && FXSELID(sel)==ID_SHUTTLE_INCREASE_RATE)
1870 	{
1871 		if(pos<0)
1872 		{ // go more leftward
1873 			shuttleDial->setValue(pos-inc);
1874 		}
1875 		else if(pos>0)
1876 		{ // go more rightward
1877 			shuttleDial->setValue(pos+inc);
1878 		}
1879 		onShuttleChange(sender,sel,ptr);
1880 	}
1881 
1882 	return 1;
1883 }
1884 
onDebugButton(FXObject * sender,FXSelector sel,void * ptr)1885 long CMainWindow::onDebugButton(FXObject *sender,FXSelector sel,void *ptr)
1886 {
1887 	CLoadedSound *s=gSoundFileManager->getActive();
1888 	if(s!=NULL)
1889 	{
1890 		if(FXSELID(sel)==ID_DEFRAG_MENUITEM)
1891 		{
1892 			s->sound->defragPoolFile();
1893 			gSoundFileManager->updateAfterEdit();
1894 		}
1895 		else if(FXSELID(sel)==ID_PRINT_SAT_MENUITEM)
1896 			s->sound->printSAT();
1897 		else if(FXSELID(sel)==ID_VERIFY_SAT_MENUITEM)
1898 			s->sound->verifySAT();
1899 	}
1900 	else
1901 		getApp()->beep();
1902 
1903 	return 1;
1904 }
1905 
1906