1 /******************************************************************************
2 * src/WMain.cpp
3 *
4 * Implementation of the main window's functions.
5 *
6 ******************************************************************************
7 * Copyright (C) 2013-2014 Timo Bingmann <tb@panthema.net>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 *****************************************************************************/
22
23 #include "WMain.h"
24 #include "SortAlgo.h"
25 #include <SDL.h>
26
27 #include "wxg/WAbout_wxg.h"
28
WMain(wxWindow * parent)29 WMain::WMain(wxWindow* parent)
30 : WMain_wxg(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE)
31 {
32 m_thread = NULL;
33 g_sound_on = false;
34
35 recordButton->Hide();
36 panelQuickSortPivot->Hide();
37 infoTextctrl->Hide();
38
39 // program icon
40 {
41 #include "sos.xpm"
42 SetIcon( wxIcon(sos) );
43 }
44
45 // program version
46 SetTitle(_("The Sound of Sorting " PACKAGE_VERSION " - http://panthema.net/2013/sound-of-sorting"));
47
48 // resize right split window
49 splitter_0->SetSashPosition(GetSize().x - 280);
50
51 // insert list of algorithms into wxListBox
52 for (const AlgoEntry* ae = g_algolist; ae != g_algolist_end; ++ae)
53 algoList->Append(ae->name);
54
55 algoList->SetSelection(0);
56
57 // insert list of data templates into wxChoice
58 wxArrayString inputList;
59 sortview->m_array.FillInputlist(inputList);
60 inputTypeChoice->Append(inputList);
61
62 sortview->m_array.FillData(0, 100);
63 inputTypeChoice->SetSelection(0);
64 arraySizeSlider->SetValue(100);
65 SetArraySize(100);
66
67 // insert quicksort pivot rules into wxChoice
68 pivotRuleChoice->Append( QuickSortPivotText() );
69 pivotRuleChoice->SetSelection(0);
70
71 // set default speed
72 speedSlider->SetValue(1000);
73 SetDelay(1000);
74
75 // set default sound sustain
76 soundSustainSlider->SetValue(700);
77 SetSoundSustain(700);
78
79 // create audio output
80 SDL_AudioSpec sdlaudiospec;
81
82 // Set the audio format
83 sdlaudiospec.freq = 44100;
84 sdlaudiospec.format = AUDIO_S16SYS;
85 sdlaudiospec.channels = 1; /* 1 = mono, 2 = stereo */
86 sdlaudiospec.samples = 4096; /* Good low-latency value for callback */
87 sdlaudiospec.callback = SoundCallback;
88 sdlaudiospec.userdata = sortview;
89
90 // Open the audio device, forcing the desired format
91 if ( SDL_OpenAudio(&sdlaudiospec, NULL) < 0 ) {
92 wxLogError(_("Couldn't open audio: ") + wxString(SDL_GetError(), wxConvISO8859_1));
93 soundButton->Disable();
94 }
95 soundButton->SetValue(false);
96
97 Show();
98
99 // start refreshing timer
100 m_reftimer = new RefreshTimer(this);
101 m_reftimer->Start(1000 / g_framerate);
102 }
103
~WMain()104 WMain::~WMain()
105 {
106 AbortAlgorithm();
107
108 SDL_CloseAudio();
109 }
110
111 BEGIN_EVENT_TABLE(WMain, WMain_wxg)
112
113 EVT_TOGGLEBUTTON(ID_RUN_BUTTON, WMain::OnRunButton)
114 EVT_BUTTON(ID_RESET_BUTTON, WMain::OnResetButton)
115 EVT_BUTTON(ID_STEP_BUTTON, WMain::OnStepButton)
116 EVT_TOGGLEBUTTON(ID_SOUND_BUTTON, WMain::OnSoundButton)
117 EVT_BUTTON(ID_RANDOM_BUTTON, WMain::OnRandomButton)
118 EVT_BUTTON(wxID_ABOUT, WMain::OnAboutButton)
119
120 EVT_COMMAND_SCROLL(ID_SPEED_SLIDER, WMain::OnSpeedSliderChange)
121 EVT_COMMAND_SCROLL(ID_SOUND_SUSTAIN_SLIDER, WMain::OnSoundSustainSliderChange)
122 EVT_COMMAND_SCROLL(ID_ARRAY_SIZE_SLIDER, WMain::OnArraySizeSliderChange)
123 EVT_LISTBOX(ID_ALGO_LIST, WMain::OnAlgoList)
124 EVT_LISTBOX_DCLICK(ID_ALGO_LIST, WMain::OnAlgoListDClick)
125
126 EVT_COMMAND(ID_RUN_FINISHED, wxEVT_COMMAND_BUTTON_CLICKED, WMain::OnRunFinished)
127
128 EVT_COMMAND(ID_INVERSION_LABEL, wxEVT_COMMAND_BUTTON_CLICKED, WMain::OnInversionLabelClick)
129
130 END_EVENT_TABLE();
131
RunAlgorithm()132 bool WMain::RunAlgorithm()
133 {
134 ASSERT(!m_thread);
135
136 if (algoList->GetSelection() == wxNOT_FOUND)
137 {
138 wxLogError(_("Please select a sorting algorithm"));
139 runButton->SetValue(false);
140 return false;
141 }
142 else
143 {
144 if (sortview->m_array.IsSorted())
145 sortview->m_array.FillData( inputTypeChoice->GetSelection(), m_array_size );
146
147 sortview->SetStepwise(false);
148
149 g_algo_name = algoList->GetStringSelection();
150 g_quicksort_pivot = (QuickSortPivotType)pivotRuleChoice->GetSelection();
151
152 m_thread = new SortAlgoThread(this, *sortview, algoList->GetSelection());
153
154 m_thread_terminate = false;
155 m_thread->Create();
156
157 g_algo_running = true;
158 m_thread->Run();
159
160 runButton->SetValue(true);
161 return true;
162 }
163 }
164
AbortAlgorithm()165 void WMain::AbortAlgorithm()
166 {
167 if (!m_thread) return;
168
169 m_thread_terminate = true;
170 if (m_thread->IsPaused()) m_thread->Resume();
171 sortview->SetStepwise(false);
172
173 m_thread->Wait();
174 g_algo_running = false;
175
176 delete m_thread;
177 m_thread = NULL;
178 }
179
OnRunButton(wxCommandEvent & event)180 void WMain::OnRunButton(wxCommandEvent &event)
181 {
182 // join finished thread
183 if (m_thread && !m_thread->IsAlive())
184 {
185 m_thread->Wait();
186 g_algo_running = false;
187
188 delete m_thread;
189 m_thread = NULL;
190 }
191
192 if (event.IsChecked())
193 {
194 if (!m_thread)
195 {
196 RunAlgorithm();
197 }
198 else
199 {
200 g_algo_running = true;
201 sortview->SetStepwise(false);
202 if (m_thread->IsPaused()) m_thread->Resume();
203 }
204 }
205 else
206 {
207 if (m_thread)
208 {
209 m_thread->Pause();
210 g_algo_running = false;
211 }
212 }
213 }
214
OnRunFinished(wxCommandEvent &)215 void WMain::OnRunFinished(wxCommandEvent&)
216 {
217 // join finished thread
218 if (m_thread)
219 {
220 m_thread->Wait();
221 g_algo_running = false;
222
223 delete m_thread;
224 m_thread = NULL;
225 }
226
227 runButton->SetValue(false);
228 }
229
OnResetButton(wxCommandEvent &)230 void WMain::OnResetButton(wxCommandEvent&)
231 {
232 // terminate running algorithm.
233 AbortAlgorithm();
234
235 runButton->SetValue(false);
236
237 sortview->m_array.FillData( inputTypeChoice->GetSelection(), m_array_size );
238 }
239
OnStepButton(wxCommandEvent &)240 void WMain::OnStepButton(wxCommandEvent&)
241 {
242 // lock thread to one step
243 if (!m_thread)
244 {
245 if (!RunAlgorithm()) return;
246 sortview->SetStepwise(true);
247 }
248 else
249 {
250 if (m_thread->IsPaused()) m_thread->Resume();
251 sortview->SetStepwise(true); // in case not already set
252 runButton->SetValue(false);
253 g_algo_running = true;
254 }
255
256 // signal to perform one step
257 sortview->DoStepwise();
258 }
259
OnSoundButton(wxCommandEvent &)260 void WMain::OnSoundButton(wxCommandEvent&)
261 {
262 if (soundButton->GetValue())
263 {
264 SoundReset();
265 SDL_PauseAudio(0);
266 g_sound_on = true;
267 }
268 else
269 {
270 g_sound_on = false;
271 SDL_PauseAudio(1);
272 }
273 }
274
OnRandomButton(wxCommandEvent &)275 void WMain::OnRandomButton(wxCommandEvent&)
276 {
277 AbortAlgorithm();
278
279 algoList->SetSelection( rand() % algoList->GetCount() );
280 sortview->m_array.FillData( inputTypeChoice->GetSelection(), m_array_size );
281
282 RunAlgorithm();
283
284 algoList->SetSelection(wxNOT_FOUND);
285 }
286
287 class WAbout : public WAbout_wxg
288 {
289 public:
WAbout(wxWindow * parent)290 WAbout(wxWindow* parent)
291 : WAbout_wxg(parent, wxID_ANY, wxEmptyString)
292 {
293 labelTitle->SetLabel(_("The Sound of Sorting " PACKAGE_VERSION));
294 labelBuildDate->SetLabel(_("Build Date: " __DATE__));
295
296 GetSizer()->Fit(this);
297 Layout();
298 Centre();
299 }
300 };
301
OnAboutButton(wxCommandEvent &)302 void WMain::OnAboutButton(wxCommandEvent&)
303 {
304 WAbout dlg(this);
305 dlg.ShowModal();
306 }
307
OnSpeedSliderChange(wxScrollEvent & event)308 void WMain::OnSpeedSliderChange(wxScrollEvent &event)
309 {
310 SetDelay(event.GetPosition());
311 }
312
SetDelay(size_t pos)313 void WMain::SetDelay(size_t pos)
314 {
315 // scale slider with this base to 10 seconds = 2 * 1000 ms
316 const double base = 4;
317
318 // different slider scale for Linux/GTK: (faster)
319 #if __WXGTK__ || MSW_PERFORMANCECOUNTER
320 g_delay = pow(base, pos / 2000.0 * log(2 * 1000.0 * 10.0) / log(base)) / 10.0;
321 #else
322 // other systems probably have sucking real-time performance anyway
323 g_delay = pow(base, pos / 2000.0 * log(2 * 1000.0) / log(base));
324 #endif
325 if (pos == 0) g_delay = 0;
326
327 if (g_delay > 10)
328 labelDelayValue->SetLabel(wxString::Format(_("%.0f ms"), g_delay));
329 else if (g_delay > 1)
330 labelDelayValue->SetLabel(wxString::Format(_("%.1f ms"), g_delay));
331 else
332 labelDelayValue->SetLabel(wxString::Format(_("%.2f ms"), g_delay));
333 }
334
OnSoundSustainSliderChange(wxScrollEvent & event)335 void WMain::OnSoundSustainSliderChange(wxScrollEvent &event)
336 {
337 SetSoundSustain(event.GetPosition());
338 }
339
OnInversionLabelClick(wxCommandEvent &)340 void WMain::OnInversionLabelClick(wxCommandEvent &)
341 {
342 sortview->m_array.ToggleCalcInversions();
343 }
344
SetSoundSustain(size_t pos)345 void WMain::SetSoundSustain(size_t pos)
346 {
347 // scale slider with this base to 50
348 const double base = 4;
349
350 g_sound_sustain = pow(base, pos / 2000.0 * log(50) / log(base));
351
352 labelSoundSustainValue->SetLabel(wxString::Format(_("%.1f"), g_sound_sustain));
353
354 labelSoundSustainValue->GetContainingSizer()->Layout();
355 }
356
OnArraySizeSliderChange(wxScrollEvent & event)357 void WMain::OnArraySizeSliderChange(wxScrollEvent &event)
358 {
359 SetArraySize(event.GetPosition());
360 }
361
SetArraySize(size_t pos)362 void WMain::SetArraySize(size_t pos)
363 {
364 // scale slider with this base to 2048
365 //const double base = 2;
366 //m_array_size = pow(base, pos / 10000.0 * log(2048) / log(base));
367 m_array_size = pos;
368
369 labelArraySizeValue->SetLabel(wxString::Format(_("%4lu"), (long unsigned)m_array_size));
370
371 labelArraySizeValue->GetContainingSizer()->Layout();
372 }
373
OnAlgoList(wxCommandEvent &)374 void WMain::OnAlgoList(wxCommandEvent&)
375 {
376 int sel = algoList->GetSelection();
377 wxString text;
378
379 bool isQuickSort = (algoList->GetStringSelection().Contains(_("Quick Sort")));
380 panelQuickSortPivot->Show(isQuickSort);
381
382 if (sel >= 0 && sel < (int)g_algolist_size && !g_algolist[sel].text.IsEmpty())
383 {
384 text = g_algolist[sel].text;
385
386 infoTextctrl->SetValue(text);
387
388 infoTextctrl->Show();
389 infoTextctrl->GetContainingSizer()->Layout();
390 }
391 else
392 {
393 infoTextctrl->Hide();
394 infoTextctrl->GetContainingSizer()->Layout();
395 }
396 }
397
OnAlgoListDClick(wxCommandEvent &)398 void WMain::OnAlgoListDClick(wxCommandEvent&)
399 {
400 // terminate running algorithm.
401 if (m_thread)
402 {
403 AbortAlgorithm();
404
405 sortview->m_array.FillData( inputTypeChoice->GetSelection(), m_array_size );
406 }
407
408 // start new one
409 RunAlgorithm();
410 }
411
412 // ----------------------------------------------------------------------------
413
RefreshTimer(WMain * wmain)414 WMain::RefreshTimer::RefreshTimer(WMain* wmain)
415 : wm(*wmain)
416 {
417 }
418
Notify()419 void WMain::RefreshTimer::Notify()
420 {
421 // update some labels with current values
422 long int accesses = g_access_count;
423 wm.labelAccessCount->SetLabel(wxString::Format(_("%ld"), accesses));
424
425 long int compares = g_compare_count;
426 wm.labelComparisonsValue->SetLabel(wxString::Format(_("%ld"), compares));
427
428 long int inversions = wm.sortview->m_array.GetInversions();
429 if (inversions >= 0)
430 wm.labelInversionCount->SetLabel(wxString::Format(_("%ld"), inversions));
431 else
432 wm.labelInversionCount->SetLabel(_("skipped"));
433
434 long int runs = wm.sortview->m_array.GetRuns();
435 wm.labelRunsCount->SetLabel(wxString::Format(_("%ld"), runs));
436
437 // repaint sortview
438 wm.sortview->Refresh(false);
439 }
440
441 // ----------------------------------------------------------------------------
442
443 class MyApp : public wxApp
444 {
445 public:
446
OnInit()447 virtual bool OnInit()
448 {
449 if (SDL_Init(SDL_INIT_AUDIO) < 0)
450 {
451 wxLogError(wxT("Couldn't initialize SDL: ") + wxString(SDL_GetError(), wxConvISO8859_1));
452 wxLog::FlushActive();
453 return false;
454 }
455
456 srand((int)wxGetLocalTime());
457
458 WMain* wmain = new WMain(NULL);
459 SetTopWindow(wmain);
460 wmain->Show();
461
462 return true;
463 }
464
OnExit()465 virtual int OnExit()
466 {
467 SDL_Quit();
468
469 // return the standard exit code
470 return wxApp::OnExit();
471 }
472 };
473
474 IMPLEMENT_APP(MyApp)
475
476 // ----------------------------------------------------------------------------
477