Const conversions for edict and kanjidic objects. Removal of obsolete dictionary...
[jben.git] / panel_kanjidrill.cpp
blob62cff2436ff0921df94c4444229d5d5d3d0460cf
1 /*
2 Project: J-Ben
3 Author: Paul Goins
4 Website: http://www.vultaire.net/software/jben/
5 License: GNU General Public License (GPL) version 2
6 (http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt)
8 File: panel_kanjidrill.cpp
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>
24 #include "panel_kanjidrill.h"
25 #include "global.h"
26 #include "maingui.h"
27 #include <stdlib.h>
28 #include <algorithm>
29 using namespace std;
31 enum {
32 /* Controls to configure the test */
33 ID_pnlConfig = 1,
34 ID_spnKanjiCount,
35 ID_sbKanjiSelect,
36 ID_rdoRandom,
37 ID_rdoStartIndex,
38 ID_spnStartIndex,
39 ID_rdoKanjiReading,
40 ID_rdoKanjiWriting,
41 ID_btnStart,
42 /* Controls for use during the test */
43 ID_pnlTest,
44 ID_txtKanji,
45 ID_txtOnyomi,
46 ID_txtKunyomi,
47 ID_txtEnglish,
48 ID_btnCorrect,
49 ID_btnWrong,
50 ID_btnStop,
51 ID_lblTestProgress
54 enum {
55 PKD_TM_Reading = 1,
56 PKD_TM_Writing
59 BEGIN_EVENT_TABLE(PanelKanjiDrill, wxPanel)
60 EVT_SPINCTRL(ID_spnKanjiCount, PanelKanjiDrill::OnKanjiCountChange)
61 EVT_RADIOBUTTON(ID_rdoRandom, PanelKanjiDrill::OnRdoRandom)
62 EVT_RADIOBUTTON(ID_rdoStartIndex, PanelKanjiDrill::OnRdoStartIndex)
63 EVT_BUTTON(ID_btnStart, PanelKanjiDrill::OnStart)
65 EVT_BUTTON(ID_btnCorrect, PanelKanjiDrill::OnCorrect)
66 EVT_BUTTON(ID_btnWrong, PanelKanjiDrill::OnWrong)
67 EVT_BUTTON(ID_btnStop, PanelKanjiDrill::OnStop)
68 END_EVENT_TABLE()
70 #if 0
71 wxMenuBar *storedBar;
72 #endif
74 PanelKanjiDrill::PanelKanjiDrill(wxWindow *owner) : RedrawablePanel(owner) {
75 /* Create test config controls */
76 pnlConfig = new wxPanel(this, ID_pnlConfig);
78 wxPanel *pnlKanjiSelect = new wxPanel(pnlConfig, wxID_ANY);
79 wxStaticText *lblKanjiCount = new wxStaticText(pnlKanjiSelect, wxID_ANY, _T("Number of kanji to test: "));
80 spnKanjiCount = new wxSpinCtrl(pnlKanjiSelect, ID_spnKanjiCount);
81 spnKanjiCount->SetValue(20);
82 rdoRandom = new wxRadioButton(pnlKanjiSelect, ID_rdoRandom, _T("Choose randomly from list"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
83 rdoStartIndex = new wxRadioButton(pnlKanjiSelect, ID_rdoStartIndex, _T("Start at index: "));
84 spnStartIndex = new wxSpinCtrl(pnlKanjiSelect, ID_spnStartIndex);
85 spnStartIndex->Enable(false);
87 wxPanel *pnlTestSelect = new wxPanel(pnlConfig, wxID_ANY);
88 rdoKanjiReading = new wxRadioButton(pnlTestSelect, ID_rdoKanjiReading, _T("Reading Kanji"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
89 rdoKanjiWriting = new wxRadioButton(pnlTestSelect, ID_rdoKanjiWriting, _T("Writing Kanji"));
91 btnStart = new wxButton(pnlConfig, ID_btnStart, _T("Start Drill"));
93 /* Create test controls */
94 pnlTest = new wxPanel(this, ID_pnlTest);
95 txtKanji = new CoveredTextBox(pnlTest, ID_txtKanji, _T("< Kanji >"));
96 wxFont fnt = txtKanji->GetFont();
97 fnt.SetPointSize(fnt.GetPointSize()+10);
98 txtKanji->SetFont(fnt);
99 txtOnyomi = new CoveredTextBox(pnlTest, ID_txtOnyomi, _T("< Onyomi >"));
100 txtKunyomi = new CoveredTextBox(pnlTest, ID_txtKunyomi, _T("< Kunyomi >"));
101 txtEnglish = new CoveredTextBox(pnlTest, ID_txtEnglish, _T("< English >"), _T("Some overly ridiculously long English string to see how resizing will work when uncovering a string which is too big for the current shape/size of the CoveredTextBox."));
102 btnCorrect = new wxButton(pnlTest, ID_btnCorrect, _T("Correct"));
103 btnWrong = new wxButton(pnlTest, ID_btnWrong, _T("Wrong"));
104 btnStop = new wxButton(pnlTest, ID_btnStop, _T("Stop Drill"));
105 lblTestProgress = new wxStaticText(pnlTest, ID_lblTestProgress, _T(""));
107 /* Create sizers */
108 /* Our custom radio box for selecting the kanji to test */
109 wxStaticBoxSizer *kanjiConfigSizer = new wxStaticBoxSizer(wxVERTICAL, pnlKanjiSelect, _T("Choose Kanji to Test"));
110 /* Row 1: Number of kanji to test */
111 wxBoxSizer *kanjiCountSizer = new wxBoxSizer(wxHORIZONTAL);
112 kanjiCountSizer->Add(lblKanjiCount, 0, wxALIGN_CENTER_VERTICAL);
113 kanjiCountSizer->Add(spnKanjiCount, 0, wxALIGN_CENTER_VERTICAL);
114 /* Row 2: Random kanji, or do range by start index */
115 wxBoxSizer *kanjiSelectSizer = new wxBoxSizer(wxVERTICAL);
116 kanjiSelectSizer->Add(rdoRandom, 1, wxALIGN_CENTER_VERTICAL);
117 wxBoxSizer *kanjiStartIndexSizer = new wxBoxSizer(wxHORIZONTAL);
118 kanjiStartIndexSizer->Add(rdoStartIndex, 0, wxALIGN_CENTER_VERTICAL);
119 kanjiStartIndexSizer->Add(spnStartIndex, 0, wxALIGN_CENTER_VERTICAL);
120 kanjiStartIndexSizer->AddStretchSpacer(1);
121 kanjiSelectSizer->Add(kanjiStartIndexSizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
122 /* Finally: Add both rows to the static box sizer */
123 kanjiConfigSizer->Add(kanjiCountSizer, 0);
124 kanjiConfigSizer->Add(kanjiSelectSizer, 0, wxEXPAND);
125 pnlKanjiSelect->SetSizer(kanjiConfigSizer);
127 /* Our custom radio box for selecting the test mode */
128 wxStaticBoxSizer *testModeSizer = new wxStaticBoxSizer(wxVERTICAL, pnlTestSelect, _T("Choose Test Type"));
129 testModeSizer->Add(rdoKanjiReading, 1);
130 testModeSizer->Add(rdoKanjiWriting, 1);
131 pnlTestSelect->SetSizer(testModeSizer);
133 /* Create any other horizontal sizers */
134 wxBoxSizer *correctWrongSizer = new wxBoxSizer(wxHORIZONTAL);
135 correctWrongSizer->Add(btnCorrect, 0, wxRIGHT, 10);
136 correctWrongSizer->Add(btnWrong);
137 correctWrongSizer->AddStretchSpacer(1);
138 correctWrongSizer->Add(btnStop);
139 /* The config sizer */
140 wxBoxSizer *configSizer = new wxBoxSizer(wxVERTICAL);
141 configSizer->Add(pnlKanjiSelect, 0, wxEXPAND | wxBOTTOM, 10);
142 configSizer->Add(pnlTestSelect, 0, wxEXPAND | wxBOTTOM, 10);
143 configSizer->Add(btnStart, 0, wxALIGN_CENTER_HORIZONTAL);
144 pnlConfig->SetSizer(configSizer);
145 /* The test mode sizer */
146 wxBoxSizer *testSizer = new wxBoxSizer(wxVERTICAL);
147 testSizer->Add(txtKanji, 0, wxEXPAND | wxBOTTOM, 10);
148 testSizer->Add(txtOnyomi, 0, wxEXPAND | wxBOTTOM, 10);
149 testSizer->Add(txtKunyomi, 0, wxEXPAND | wxBOTTOM, 10);
150 testSizer->Add(txtEnglish, 0, wxEXPAND | wxBOTTOM, 10);
151 testSizer->Add(correctWrongSizer, 0, wxEXPAND);
152 testSizer->AddStretchSpacer(1);
153 testSizer->Add(lblTestProgress, 0, wxEXPAND);
154 pnlTest->SetSizer(testSizer);
155 /* The panel-wide sizer */
156 wxBoxSizer *panelSizer = new wxBoxSizer(wxVERTICAL);
157 panelSizer->Add(pnlConfig, 1, wxEXPAND | wxALL, 10);
158 panelSizer->Add(pnlTest, 1, wxEXPAND | wxALL, 10);
160 testing=false;
161 pnlTest->Show(false); /* Set the test panel as invisible at the beginning */
162 this->SetSizerAndFit(panelSizer); /* Now apply the sizer afterwards so layout occurs properly. */
165 void PanelKanjiDrill::OnStart(wxCommandEvent& ev) {
166 /* Placeholder code */
167 /* We need to do the following:
168 0. (Possibly) Verify that test mode is NOT enabled when this event is triggered
169 (In case the button can somehow be pushed when not visible.) (Done)
170 0.5. (Possibly) Validate the input of the user. Might not be necessary if all controls limit input properly.
171 (They do now.)
172 1. Reset any test variables
173 2. Create kanji test list based upon selected options
174 3. Load up the first kanji to be tested
175 4. Switch to the test GUI */
177 unsigned int i, startIndex;
178 if(!testing) {
179 /* Reset test variables */
180 currentKanjiIndex = -1;
181 extraPractice = false;
182 lastWasCorrect = false; /* used for Extra Practice mode only */
183 totalToTest = spnKanjiCount->GetValue();
184 testKanji.clear();
185 missedKanji.clear();
186 correct = totalTested = 0;
187 if(rdoKanjiReading->GetValue()) testMode = PKD_TM_Reading;
188 else if(rdoKanjiWriting->GetValue()) testMode = PKD_TM_Writing;
189 else {
190 wxMessageBox(_T("Unknown test mode selected!"), _T("Error!"), wxOK | wxICON_ERROR, this);
191 return;
194 /* Create kanji test list based upon selected options */
195 if(rdoStartIndex->GetValue()) {
196 /* Copy from study list at specified index */
197 startIndex = spnStartIndex->GetValue() - 1; /* Remember, the GUI uses a 1-base. */
198 for(i=startIndex; i < startIndex + totalToTest; i++)
199 testKanji.push_back((*jben->kanjiList)[i]);
200 } else {
201 /* Random character selection */
202 vector<wxChar> localVector = jben->kanjiList->GetVector();
203 wxChar c;
204 while(testKanji.size()<totalToTest) {
205 i = rand() % localVector.size();
206 c = localVector[i];
207 testKanji.push_back(c);
208 localVector.erase(localVector.begin() + i);
212 /* Debug: Show our list of kanji to test */
213 #ifdef DEBUG
214 printf("DEBUG: Kanji to test: ");
215 for(vector<wxChar>::iterator vi=testKanji.begin();vi!=testKanji.end();vi++)
216 printf("%lc", *vi);
217 printf("\n");
218 #endif
220 /* Load up the first kanji */
221 ShowNextKanji();
223 /* Switch the displayed panel */
224 jben->gui->GetMenuBar()->EnableTop(GUI_menuKanji, false);
225 pnlConfig->Show(false);
226 pnlTest->Show(true);
227 pnlTest->SetFocus();
228 this->GetSizer()->Layout();
229 testing=true;
233 void PanelKanjiDrill::Stop() {
234 double score=0.0;
235 if(totalTested>0) score = round(100000*
236 (double)correct/
237 (double)totalTested)/1000;
238 wxString kanjiMissed;
239 for(vector<wxChar>::iterator vi=missedKanji.begin(); vi!=missedKanji.end(); vi++)
240 kanjiMissed.append(*vi);
242 /* Display test results */
243 if(totalTested==totalToTest) {
244 wxMessageBox(
245 wxString::Format(_T("TEST COMPLETED\n\nFinal Score: %d/%d (%0.3f%%)\nKanji Missed: %s"),
246 correct, totalTested, score, kanjiMissed.c_str()),
247 _T("Test Results"), wxOK | wxICON_INFORMATION, this);
248 } else {
249 wxMessageBox(
250 wxString::Format(_T("TEST INCOMPLETE\n\nScore: %d/%d (%0.3f%%)\nTest Progress: %d/%d (%0.3f%%)\nKanji Missed: %s"),
251 correct, totalTested, score, totalTested, totalToTest,
252 round(100000*(double)totalTested/(double)totalToTest)/1000,
253 kanjiMissed.c_str()),
254 _T("Test Results"), wxOK | wxICON_INFORMATION, this);
257 /* Switch the displayed panel */
258 pnlTest->Show(false);
259 pnlConfig->Show(true);
260 pnlConfig->SetFocus();
261 this->GetSizer()->Layout();
262 jben->gui->GetMenuBar()->EnableTop(GUI_menuKanji, true);
263 testing=false;
266 void PanelKanjiDrill::OnStop(wxCommandEvent& ev) {
267 if(testing) {
268 /* If testing is not complete, prompt to make sure we want to quit. */
269 int result = wxYES;
270 if(testKanji.size()!=0) {
271 result = wxMessageBox(_T("A test is still in progress! Are you sure?"), _T("Test in progress!"), wxYES_NO | wxICON_EXCLAMATION, this);
273 if(result==wxYES) {
274 Stop();
279 bool PanelKanjiDrill::TestInProgress() {return testing;}
281 void PanelKanjiDrill::UpdateKanjiCountSpinner() {
282 int max = jben->kanjiList->Size();
283 if(max>0) {
284 spnKanjiCount->SetRange(1, max);
285 int i = spnKanjiCount->GetValue();
286 if(i<1) {i=1; spnKanjiCount->SetValue(1);}
287 if(i>max) {i=max; spnKanjiCount->SetValue(max);}
291 void PanelKanjiDrill::UpdateStartIndexSpinner() {
292 int max = jben->kanjiList->Size();
293 int i = spnKanjiCount->GetValue();
294 int indexMax = max - (i-1);
295 spnStartIndex->SetRange(1, indexMax);
296 i = spnStartIndex->GetValue();
297 if(i<1) spnStartIndex->SetValue(1);
298 if(i>indexMax) spnStartIndex->SetValue(indexMax);
301 void PanelKanjiDrill::OnKanjiCountChange(wxSpinEvent& ev) {
302 UpdateStartIndexSpinner();
305 void PanelKanjiDrill::Redraw() {
306 if(!testing) {
307 int max = jben->kanjiList->Size();
308 if(max>0) {
309 UpdateKanjiCountSpinner();
310 UpdateStartIndexSpinner();
311 this->Enable(true);
312 } else {
313 this->Enable(false);
318 void PanelKanjiDrill::OnRdoRandom(wxCommandEvent& ev) {
319 spnStartIndex->Enable(false);
322 void PanelKanjiDrill::OnRdoStartIndex(wxCommandEvent& ev) {
323 spnStartIndex->Enable(true);
326 void PanelKanjiDrill::ShowNextKanji() {
327 /* Remove the current kanji, and get the new one */
328 if(!extraPractice) {
329 if(currentKanjiIndex!=-1)
330 testKanji.erase(testKanji.begin()+currentKanjiIndex);
331 if(testKanji.size()>0) {
332 currentKanjiIndex = rand() % testKanji.size();
333 currentKanji = testKanji[currentKanjiIndex];
334 } else { /* If there's no more kanji to get... */
335 if(missedKanji.size()>0) {
336 extraPractice = true;
337 testKanji = missedKanji;
339 else Stop();
342 if(extraPractice) {
343 if(lastWasCorrect)
344 testKanji.erase(testKanji.begin()+currentKanjiIndex);
345 if(testKanji.size()>0) {
346 currentKanjiIndex = rand() % testKanji.size();
347 currentKanji = testKanji[currentKanjiIndex];
348 } else { /* If there's no more kanji to get... */
349 Stop();
353 /* Update CoveredTextBoxes with new info from KANJIDIC. */
354 txtKanji->Cover();
355 txtOnyomi->Cover();
356 txtKunyomi->Cover();
357 txtEnglish->Cover();
358 txtKanji->SetHiddenStr(wxString(currentKanji));
359 txtOnyomi->SetHiddenStr(jben->dicts->GetKanjidic()
360 ->GetOnyomiStr(currentKanji));
361 txtKunyomi->SetHiddenStr(jben->dicts->GetKanjidic()
362 ->GetKunyomiStr(currentKanji));
363 txtEnglish->SetHiddenStr(jben->dicts->GetKanjidic()
364 ->GetEnglishStr(currentKanji));
366 /* Update the test status label */
367 double score=0.0;
368 if(totalTested>0) score = round(100000*
369 (double)correct/
370 (double)totalTested)/1000;
371 if(!extraPractice) {
372 lblTestProgress->SetLabel(wxString::Format(
373 _T("Current score: %d/%d (%0.3f%%) Test Progress: %d/%d (%0.3f%% done)"),
374 correct, totalTested, score,
375 totalTested, totalToTest, round(100000*
376 double(totalTested)/
377 double(totalToTest))/1000));
378 } else {
379 lblTestProgress->SetLabel(wxString::Format(
380 _T("Extra practice: %d remaining Final score: %d/%d (%0.3f%%)"),
381 testKanji.size(), correct, totalTested, score));
384 /* Refresh layout */
385 this->GetSizer()->Layout();
387 /* Uncover the field(s) appropriate for the current test mode. */
388 switch(testMode) {
389 case PKD_TM_Reading:
390 txtKanji->Uncover();
391 break;
392 case PKD_TM_Writing:
393 txtOnyomi->Uncover();
394 txtKunyomi->Uncover();
395 txtEnglish->Uncover();
396 break;
400 void PanelKanjiDrill::OnCorrect(wxCommandEvent& ev) {
401 if(!extraPractice) {
402 totalTested++;
403 correct++;
404 } else lastWasCorrect = true;
405 ShowNextKanji();
408 void PanelKanjiDrill::OnWrong(wxCommandEvent& ev) {
409 if(!extraPractice) {
410 totalTested++;
411 missedKanji.push_back(currentKanji);
412 } else lastWasCorrect = false;
413 ShowNextKanji();