1
//--------------------------------------------------------------------------------------
2
// LenMus Phonascus: The teacher of music
3
// Copyright (c) 2002-2007 Cecilio Salmeron
5
// This program is free software; you can redistribute it and/or modify it under the
6
// terms of the GNU General Public License as published by the Free Software Foundation;
7
// either version 2 of the License, or (at your option) any later version.
9
// This program is distributed in the hope that it will be useful, but WITHOUT ANY
10
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11
// PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
// You should have received a copy of the GNU General Public License along with this
14
// program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
15
// Fifth Floor, Boston, MA 02110-1301, USA.
17
// For any comment, suggestion or feature request, please contact the manager of
18
// the project at cecilios@users.sourceforge.net
20
//-------------------------------------------------------------------------------------
21
/*! @file TheoKeySignCtrol.cpp
22
@brief Implementation file for class lmTheoKeySignCtrol
23
@ingroup html_controls
25
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
26
#pragma implementation "TheoKeySignCtrol.h"
29
// For compilers that support precompilation, includes "wx.h".
30
#include "wx/wxprec.h"
36
#include "TheoKeySignCtrol.h"
37
#include "UrlAuxCtrol.h"
38
#include "Constrains.h"
39
#include "Generators.h"
40
#include "../auxmusic/Conversion.h"
42
#include "../globals/Colors.h"
43
extern lmColors* g_pColors;
45
// access to global external variables
46
extern bool g_fReleaseVersion; // in TheApp.cpp
47
extern bool g_fReleaseBehaviour; // in TheApp.cpp
48
extern bool g_fShowDebugLinks; // in TheApp.cpp
49
extern bool g_fAutoNewProblem; // in Preferences.cpp
51
//access to error's logger
52
#include "../app/Logger.h"
53
extern lmLogger* g_pLogger;
57
//------------------------------------------------------------------------------------
58
// Implementation of lmTheoKeySignCtrol
64
const int BUTTONS_DISTANCE = 5; //pixels
65
const int NUM_LINKS = 3; //links for actions
69
ID_LINK_SEE_SOURCE = 3000,
72
ID_LINK = ID_BUTTON + lmTHEO_KEYSIGN_NUM_BUTTONS,
77
BEGIN_EVENT_TABLE(lmTheoKeySignCtrol, wxWindow)
78
EVT_COMMAND_RANGE (ID_BUTTON, ID_BUTTON+lmTHEO_KEYSIGN_NUM_BUTTONS-1, wxEVT_COMMAND_BUTTON_CLICKED, lmTheoKeySignCtrol::OnRespButton)
79
EVT_SIZE (lmTheoKeySignCtrol::OnSize)
81
LM_EVT_URL_CLICK (ID_LINK_SEE_SOURCE, lmTheoKeySignCtrol::OnDebugShowSourceScore)
82
LM_EVT_URL_CLICK (ID_LINK_DUMP, lmTheoKeySignCtrol::OnDebugDumpScore)
84
LM_EVT_URL_CLICK (ID_LINK_NEW_PROBLEM, lmTheoKeySignCtrol::OnNewProblem)
85
LM_EVT_URL_CLICK (ID_LINK_SOLUTION, lmTheoKeySignCtrol::OnDisplaySolution)
88
IMPLEMENT_CLASS(lmTheoKeySignCtrol, wxWindow)
90
static wxString sMajor[15];
91
static wxString sMinor[15];
94
lmTheoKeySignCtrol::lmTheoKeySignCtrol(wxWindow* parent, wxWindowID id,
95
lmTheoKeySignConstrains* pConstrains,
96
const wxPoint& pos, const wxSize& size, int style)
97
: wxWindow(parent, id, pos, size, style )
100
SetBackgroundColour(*wxWHITE);
101
for (int i=0; i < lmTHEO_KEYSIGN_NUM_BUTTONS; i++) {
102
m_pAnswerButton[i] = (wxButton*)NULL;
105
m_fButtonsEnabled = false;
106
m_fProblemCreated = false;
107
m_pScore = (lmScore*)NULL;
108
m_pScoreCtrol = (lmScoreAuxCtrol*)NULL;
109
m_pConstrains = pConstrains;
111
//language dependent strings. Can not be statically initiallized because
112
//then they do not get translated
113
sMajor[0] = _("C major");
114
sMajor[1] = _("G major");
115
sMajor[2] = _("D major");
116
sMajor[3] = _("A major");
117
sMajor[4] = _("E major");
118
sMajor[5] = _("B major");
119
sMajor[6] = _("F # major");
120
sMajor[7] = _("C # major");
121
sMajor[8] = _("C b major");
122
sMajor[9] = _("G b major");
123
sMajor[10] = _("D b major");
124
sMajor[11] = _("A b major");
125
sMajor[12] = _("E b major");
126
sMajor[13] = _("B b major");
127
sMajor[14] = _("F major");
129
sMinor[0] = _("A minor");
130
sMinor[1] = _("E minor");
131
sMinor[2] = _("B minor");
132
sMinor[3] = _("F # minor");
133
sMinor[4] = _("C # minor");
134
sMinor[5] = _("G # minor");
135
sMinor[6] = _("D # minor");
136
sMinor[7] = _("A # minor");
137
sMinor[8] = _("A b minor");
138
sMinor[9] = _("E b minor");
139
sMinor[10] = _("B b minor");
140
sMinor[11] = _("F minor");
141
sMinor[12] = _("C minor");
142
sMinor[13] = _("G minor");
143
sMinor[14] = _("D minor");
145
//the window is divided into two regions: top, for score on left and counters and links
146
//on the right, and bottom region, for answer buttons
147
wxBoxSizer* pMainSizer = new wxBoxSizer( wxVERTICAL );
150
if (g_fShowDebugLinks && !g_fReleaseVersion) {
151
wxBoxSizer* pDbgSizer = new wxBoxSizer( wxHORIZONTAL );
152
pMainSizer->Add(pDbgSizer, 0, wxALIGN_LEFT|wxLEFT|wxRIGHT, 5);
154
// "See source score"
156
new lmUrlAuxCtrol(this, ID_LINK_SEE_SOURCE, _("See source score") ),
157
wxSizerFlags(0).Left().Border(wxALL, 10) );
160
new lmUrlAuxCtrol(this, ID_LINK_DUMP, _("Dump score") ),
161
wxSizerFlags(0).Left().Border(wxALL, 10) );
164
// sizer for the scoreCtrol and the CountersCtrol
165
wxBoxSizer* pTopSizer = new wxBoxSizer( wxHORIZONTAL );
168
wxSizerFlags(0).Left().Border(wxLEFT|wxRIGHT, 10) );
171
m_pScoreCtrol = new lmScoreAuxCtrol(this, -1, m_pScore, wxDefaultPosition, wxSize(350,150), eSIMPLE_BORDER);
172
pTopSizer->Add(m_pScoreCtrol,
173
wxSizerFlags(1).Left().Border(wxTOP|wxBOTTOM, 10));
174
m_pScoreCtrol->SetMargins(lmToLogicalUnits(10, lmMILLIMETERS), //left=1cm
175
lmToLogicalUnits(10, lmMILLIMETERS), //right=1cm
176
lmToLogicalUnits(10, lmMILLIMETERS)); //top=1cm
177
m_pScoreCtrol->SetScale((float)1.3);
179
// right/wrong answers counters control
180
m_pCounters = new lmCountersCtrol(this, wxID_ANY);
183
wxSizerFlags(0).Left().Border(wxLEFT|wxRIGHT, 10) );
188
wxBoxSizer* pLinksSizer = new wxBoxSizer( wxHORIZONTAL );
191
wxSizerFlags(0).Center().Border(wxALL, 10) );
193
// "new problem" button
195
new lmUrlAuxCtrol(this, ID_LINK_NEW_PROBLEM, _("New problem") ),
196
wxSizerFlags(0).Left().Border(wxLEFT|wxRIGHT|wxBOTTOM, 20) );
198
// "show solution" button
200
new lmUrlAuxCtrol(this, ID_LINK_SOLUTION, _("Show solution") ),
201
wxSizerFlags(0).Left().Border(wxLEFT|wxRIGHT|wxBOTTOM, 20) );
204
//create 15 buttons for the answers: three rows, five buttons per row
205
wxBoxSizer* pRowSizer;
208
const int NUM_ROWS = 3;
209
const int NUM_COLS = 5;
210
for (int iRow=0; iRow < NUM_ROWS; iRow++) {
211
pRowSizer = new wxBoxSizer( wxHORIZONTAL );
214
wxSizerFlags(0).Left());
215
pRowSizer->Add(20+BUTTONS_DISTANCE, 24, 0); //spacer to center labels
217
for (int iCol=0; iCol < NUM_COLS; iCol++) {
218
iB = iCol + iRow * NUM_COLS; // button index: 0 .. 24
219
pButton = new wxButton( this, ID_BUTTON + iB, _T(""),
220
wxDefaultPosition, wxSize(90, 24));
221
m_pAnswerButton[iB++] = pButton;
224
wxSizerFlags(0).Border(wxALL, BUTTONS_DISTANCE) );
228
SetSizer( pMainSizer ); // use the sizer for window layout
229
pMainSizer->SetSizeHints( this ); // set size hints to honour minimum size
234
lmTheoKeySignCtrol::~lmTheoKeySignCtrol()
237
delete m_pScoreCtrol;
238
m_pScoreCtrol = (lmScoreAuxCtrol*)NULL;
242
delete m_pConstrains;
243
m_pConstrains = (lmTheoKeySignConstrains*) NULL;
248
m_pScore = (lmScore*)NULL;
252
void lmTheoKeySignCtrol::EnableButtons(bool fEnable)
254
for (int i=0; i < lmTHEO_KEYSIGN_NUM_BUTTONS; i++) {
255
if (m_pAnswerButton[i])
256
m_pAnswerButton[i]->Enable(fEnable);
258
m_fButtonsEnabled = fEnable;
262
//----------------------------------------------------------------------------------------
265
void lmTheoKeySignCtrol::OnSize(wxSizeEvent& event)
267
//wxLogMessage(_T("OnSize en IntrervalsControl"));
272
void lmTheoKeySignCtrol::OnNewProblem(wxCommandEvent& event)
277
void lmTheoKeySignCtrol::OnDisplaySolution(wxCommandEvent& event)
279
m_pCounters->IncrementWrong();
281
EnableButtons(false); //student must not give now the answer
284
void lmTheoKeySignCtrol::OnRespButton(wxCommandEvent& event)
286
int nIndex = event.GetId() - ID_BUTTON;
288
//verify if success or failure
289
bool fSuccess = (nIndex == m_nIndexKeyName);
291
//produce feedback sound, and update counters
293
m_pCounters->IncrementRight();
295
m_pCounters->IncrementWrong();
298
//if failure or not auto-new problem, display the solution.
299
//Else, if success and auto-new problem, generate a new problem
300
if (!fSuccess || !g_fAutoNewProblem) {
302
//failure: mark wrong button in red and right one in green
303
m_pAnswerButton[m_nIndexKeyName]->SetBackgroundColour(g_pColors->Success());
304
m_pAnswerButton[nIndex]->SetBackgroundColour(g_pColors->Failure());
308
EnableButtons(false);
316
void lmTheoKeySignCtrol::NewProblem()
322
lmRandomGenerator oGenerator;
323
if (m_pConstrains->GetScaleMode() == eMayorAndMinorModes) {
324
m_fMajorMode = oGenerator.FlipCoin();
327
m_fMajorMode = (m_pConstrains->GetScaleMode() == eMajorMode);
330
// choose key signature and prepare answer
331
bool fFlats = oGenerator.FlipCoin();
333
int nAccidentals = oGenerator.RandomNumber(0, m_pConstrains->GetMaxAccidentals());
342
nAnswer = 0; // Do Mayor, La menor, no accidentals
347
nAnswer = 14; // Fa Mayor, Re menor"
352
nAnswer = 13; // Si b Mayor, Sol menor"
353
m_nIndexKeyName = 13;
357
nAnswer = 12; // Mi b Mayor, Do menor"
362
nAnswer = 11; // La b Mayor, Fa menor"
363
m_nIndexKeyName = 11;
367
nAnswer = 10; // Re b Mayor, Si b menor"
372
nAnswer = 9; // Sol b Mayor, Mi b menor"
377
nAnswer = 8; // Do b Mayor, La b menor"
382
// Major mode, sharps
387
nAnswer = 0; // Do Mayor, La menor"
392
nAnswer = 1; // Sol Mayor, Mi menor"
393
m_nIndexKeyName = 10;
397
nAnswer = 2; // Re Mayor, Si menor"
402
nAnswer = 3; // La Mayor, Fa # menor"
403
m_nIndexKeyName = 12;
407
nAnswer = 4; // Mi Mayor, Do # menor"
412
nAnswer = 5; // Si Mayor, Sol # menor"
413
m_nIndexKeyName = 14;
417
nAnswer = 6; // Fa # Mayor, Re # menor"
422
nAnswer = 7; // Do # Mayor, La # menor"
434
nAnswer = 0; // Do Mayor, La menor"
435
m_nIndexKeyName = 11;
439
nAnswer = 14; // Fa Mayor, Re menor"
444
nAnswer = 13; // Si b Mayor, Sol menor"
449
nAnswer = 12; // Mi b Mayor, Do menor"
454
nAnswer = 11; // La b Mayor, Fa menor"
459
nAnswer = 10; // Re b Mayor, Si b menor"
460
m_nIndexKeyName = 13;
464
nAnswer = 9; // Sol b Mayor, Mi b menor"
469
nAnswer = 8; // Do b Mayor, La b menor"
470
m_nIndexKeyName = 10;
474
// Minor mode, sharps
479
nAnswer = 0; // Do Mayor, La menor"
480
m_nIndexKeyName = 11;
484
nAnswer = 1; // Sol Mayor, Mi menor"
489
nAnswer = 2; // Re Mayor, Si menor"
490
m_nIndexKeyName = 14;
494
nAnswer = 3; // La Mayor, Fa # menor"
499
nAnswer = 4; // Mi Mayor, Do # menor"
504
nAnswer = 5; // Si Mayor, Sol # menor"
509
nAnswer = 6; // Fa # Mayor, Re # menor"
514
nAnswer = 7; // Do # Mayor, La # menor"
515
m_nIndexKeyName = 12;
521
// choose type of problem
522
if (m_pConstrains->GetProblemType() == eBothKeySignProblems) {
523
m_fIdentifyKey = oGenerator.FlipCoin();
526
m_fIdentifyKey = (m_pConstrains->GetProblemType() == eIdentifyKeySignature);
529
g_pLogger->LogTrace(_T("lmTheoKeySignCtrol"),
530
_T("[lmTheoKeySignCtrol::NewProblem] m_fIdentifyKey=%s, m_fMajorMode=%s, fFlats=%s, nKey=%d, nAnswer=%d, m_nIndexKeyName=%d"),
531
(m_fIdentifyKey ? _T("yes") : _T("no")),
532
(m_fMajorMode ? _T("yes") : _T("no")),
533
(fFlats ? _T("yes") : _T("no")),
534
nKey, nAnswer, m_nIndexKeyName);
537
// store index to right answer button (for guess-number-of-accidentals problems)
538
if (!m_fIdentifyKey) {
539
m_nIndexKeyName = KeySignatureToNumFifths(nKey);
540
if (m_nIndexKeyName < 0) m_nIndexKeyName = 7 - m_nIndexKeyName;
544
EClefType nClef = oGenerator.GenerateClef(m_pConstrains->GetClefConstrains());
546
// write buttons' labels, depending on mode
547
if (m_fIdentifyKey) {
549
m_pAnswerButton[0]->SetLabel(_("C flat major"));
550
m_pAnswerButton[1]->SetLabel(_("C major"));
551
m_pAnswerButton[2]->SetLabel(_("C sharp major"));
552
m_pAnswerButton[3]->SetLabel(_("D flat major"));
553
m_pAnswerButton[4]->SetLabel(_("D major"));
554
m_pAnswerButton[5]->SetLabel(_("E flat major"));
555
m_pAnswerButton[6]->SetLabel(_("E major"));
556
m_pAnswerButton[7]->SetLabel(_("F major"));
557
m_pAnswerButton[8]->SetLabel(_("F sharp major"));
558
m_pAnswerButton[9]->SetLabel(_("G flat major"));
559
m_pAnswerButton[10]->SetLabel(_("G major"));
560
m_pAnswerButton[11]->SetLabel(_("A flat major"));
561
m_pAnswerButton[12]->SetLabel(_("A major"));
562
m_pAnswerButton[13]->SetLabel(_("B flat major"));
563
m_pAnswerButton[14]->SetLabel(_("B major"));
564
//14,0,7,12,2,10,4,8,6,13,1,11,3,9,5
567
m_pAnswerButton[0]->SetLabel(_("C minor"));
568
m_pAnswerButton[1]->SetLabel(_("C sharp minor"));
569
m_pAnswerButton[2]->SetLabel(_("D minor"));
570
m_pAnswerButton[3]->SetLabel(_("D sharp minor"));
571
m_pAnswerButton[4]->SetLabel(_("E flat minor"));
572
m_pAnswerButton[5]->SetLabel(_("E minor"));
573
m_pAnswerButton[6]->SetLabel(_("F minor"));
574
m_pAnswerButton[7]->SetLabel(_("F sharp minor"));
575
m_pAnswerButton[8]->SetLabel(_("G minor"));
576
m_pAnswerButton[9]->SetLabel(_("G sharp minor"));
577
m_pAnswerButton[10]->SetLabel(_("A flat minor"));
578
m_pAnswerButton[11]->SetLabel(_("A minor"));
579
m_pAnswerButton[12]->SetLabel(_("A sharp minor"));
580
m_pAnswerButton[13]->SetLabel(_("B flat minor"));
581
m_pAnswerButton[14]->SetLabel(_("B minor"));
585
// type of problem: write key
586
m_pAnswerButton[0]->SetLabel(_("No accidentals"));
587
m_pAnswerButton[1]->SetLabel(_("1 #"));
588
m_pAnswerButton[2]->SetLabel(_("2 #"));
589
m_pAnswerButton[3]->SetLabel(_("3 #"));
590
m_pAnswerButton[4]->SetLabel(_("4 #"));
591
m_pAnswerButton[5]->SetLabel(_("5 #"));
592
m_pAnswerButton[6]->SetLabel(_("6 #"));
593
m_pAnswerButton[7]->SetLabel(_("7 #"));
594
m_pAnswerButton[8]->SetLabel(_("1 b"));
595
m_pAnswerButton[9]->SetLabel(_("2 b"));
596
m_pAnswerButton[10]->SetLabel(_("3 b"));
597
m_pAnswerButton[11]->SetLabel(_("4 b"));
598
m_pAnswerButton[12]->SetLabel(_("5 b"));
599
m_pAnswerButton[13]->SetLabel(_("6 b"));
600
m_pAnswerButton[14]->SetLabel(_("7 b"));
604
m_pScore = new lmScore();
605
m_pScore->SetTopSystemDistance( lmToLogicalUnits(5, lmMILLIMETERS) ); //5mm
606
m_pScore->AddInstrument(1,0,0,_T("")); //one vstaff, MIDI channel 0, MIDI instr 0
607
lmVStaff *pVStaff = m_pScore->GetVStaff(1, 1); //get first vstaff of instr.1
608
pVStaff->AddClef( nClef );
609
pVStaff->AddKeySignature(nKey);
610
pVStaff->AddBarline(etb_EndBarline, lmNO_VISIBLE);
612
//wxLogMessage(wxString::Format(
613
// _T("[lmTheoKeySignCtrol::NewProblem] m_nIndexKeyName=%d, oIntv.GetIntervalNum()=%d"),
614
// m_nIndexKeyName, oIntv.GetIntervalNum() ));
616
//display the problem
617
m_pCounters->NextTeam();
618
if (m_fIdentifyKey) {
620
m_sAnswer = sMajor[nAnswer] + _T(", ") + sMinor[nAnswer];
621
m_pScoreCtrol->DisplayScore(m_pScore);
624
m_sAnswer = (m_fMajorMode ? sMajor[nAnswer] : sMinor[nAnswer] );
625
m_pScoreCtrol->DisplayMessage(m_sAnswer, lmToLogicalUnits(5, lmMILLIMETERS));
627
m_fProblemCreated = true;
632
void lmTheoKeySignCtrol::DisplaySolution()
634
if (m_fIdentifyKey) {
635
m_pScoreCtrol->DisplayMessage(m_sAnswer, lmToLogicalUnits(5, lmMILLIMETERS), false);
637
m_pScoreCtrol->DisplayScore(m_pScore, false);
640
m_fProblemCreated = false;
644
void lmTheoKeySignCtrol::OnDebugShowSourceScore(wxCommandEvent& event)
646
m_pScoreCtrol->SourceLDP();
649
void lmTheoKeySignCtrol::OnDebugDumpScore(wxCommandEvent& event)
651
m_pScoreCtrol->Dump();
654
void lmTheoKeySignCtrol::ResetExercise()
656
for (int i=0; i < lmTHEO_KEYSIGN_NUM_BUTTONS; i++) {
657
if (m_pAnswerButton[i]) {
658
m_pAnswerButton[i]->SetBackgroundColour( g_pColors->Normal() );
661
EnableButtons(false);
665
m_pScore = (lmScore*)NULL;