summaryrefslogtreecommitdiffstats
path: root/ktouch/src/ktouch.cpp
blob: 632aabe11fcd582cb1d66a2168f328ead2e4f181 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
/***************************************************************************
 *   ktouch.cpp                                                            *
 *   ----------                                                            *
 *   Copyright (C) 2000 by Håvard Frøiland, 2004 by Andreas Nicolai        *
 *   ghorwin@users.sourceforge.net                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "ktouch.h"
#include "ktouch.moc"

// Standard files
#include <algorithm>

// QT Header
#include <tqvbox.h>
#include <tqsignalmapper.h>
#include <tqcheckbox.h>
#include <tqlabel.h>
#include <tqgroupbox.h>
//#include <qimevent.h>

// KDE Header
#include <klocale.h>
#include <kstatusbar.h>
#include <kfiledialog.h>
#include <kaction.h>
#include <kstandarddirs.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <kpopupmenu.h>
#include <kconfigdialog.h>
#include <kaction.h>
#include <kcombobox.h>
#include <kfontrequester.h>
#include <knuminput.h>

// Own header files
#include "ktouchlecture.h"
#include "ktouchlectureeditor.h"
#include "ktouchstatus.h"
#include "ktouchslideline.h"
#include "ktouchkeyboardwidget.h"
#include "ktouchcoloreditor.h"
#include "ktouchtrainer.h"
#include "ktouchstatistics.h"
#include "ktouchprefgenerallayout.h"
#include "ktouchpreftraininglayout.h"
#include "ktouchprefkeyboardlayout.h"
#include "ktouchprefcolorslayout.h"
#include "ktouchutils.h"
#include "prefs.h"
#include "ktouchcolorscheme.h"

KTouch * KTouchPtr = NULL;

KTouch::KTouch()
  : KMainWindow( 0, "KTouch" ),
    m_statusWidget(NULL),
    m_keyboardWidget(NULL),
	m_trainer(NULL)
{
	setFocusPolicy(StrongFocus);
	setInputMethodEnabled(true);

	// Set global KTouchPtr to the main KTouch Object
	KTouchPtr = this;
	// General initialization of the program, common for all start modes
	init();
	// Setup our actions and connections
	setupActions();
	// create the GUI reading the ui.rc file
	if (!initialGeometrySet())
		resize( TQSize(700, 510).expandedTo(minimumSizeHint()));
	setupGUI(ToolBar | Keys | StatusBar | Create);
	setAutoSaveSettings();
	// Read user statistics
	KURL stat_file = KGlobal::dirs()->findResource("data", "ktouch/statistics.xml");
	//kdDebug() << "[KTouch::KTouch]  readings statistics from file '" << stat_file << "'" << endl;
	if (!m_stats.read(this, stat_file))
		m_stats.clear();	// if we can't read it, start with empty statistics

	// Init a training session
	initTrainingSession();
	
	// If session was restored, the function readProperties() was already called
    if (kapp->isRestored()) {
		kdDebug() << "[KTouch::KTouch]  restoring session..." << endl;
		/// \todo Rewrite all the session management stuff.
		///       For now we just do the same as for the standard startup.
	}
//	else {
		//kdDebug() << "[KTouch::KTouch]  starting standard training..." << endl;
		// A note about safety: In this function there are a lot of things that might go
		// wrong. What happens if the training file can't be found? What if the
		// file cannot be opened or is corrupt? Whatever happens, the function loadXML() 
		// ensures, that there is at least the default mini-level in the lecture
		// so that the training won't crash.

		// Load statistics data from statistics-file
		
	    // Reload the last used training file.
		if (Prefs::currentLectureFile().isNull() ||
		    !m_lecture.loadXML(this, Prefs::currentLectureFile() ))
		{
			Prefs::setCurrentLectureFile(TQString::null);
			m_defaultLectureAction->setCurrentItem(-1);
		}
		else {
			updateFontFromLecture();
			// adjust check marks in quick-select menus
			updateLectureActionCheck();
			//kdDebug() << "[KTouch::KTouch]  lecture file = " << Prefs::currentLectureFile() << endl;
		}
		
		// Adjust check mark for the keyboard file
		updateKeyboardActionCheck();
		// If the user doesn't want to restart with his old level, start from 0 (level 1)
        if (!Prefs::rememberLevel())	m_trainer->m_level = 0;
		else							m_trainer->m_level = Prefs::currentTrainingLevel();
        // now let's start the training in the current level
		m_trainer->startTraining(true);
//	}
}
// ----------------------------------------------------------------------------

// Free memory of objects that are not owned by the main KTouch object
KTouch::~KTouch() {
	delete m_trainer;
	m_trainer = NULL;
}
// ----------------------------------------------------------------------------

KTouchLectureStats& KTouch::getCurrentLectureStats() {
//	kdDebug() << "[KTouch::getCurrentLectureStats]  " << endl;
	KURL lectureURL = Prefs::currentLectureFile();
	if (lectureURL.url().isEmpty())  lectureURL = "default";
//	kdDebug() << "  lectureURL = '" << lectureURL << "'" << endl;
	KTouchLectureStats& stat = m_stats.m_lectureStats[lectureURL];
	// add additional info to the statistics
	if (stat.m_lectureURL.isEmpty())
		stat.m_lectureURL = lectureURL;
	if (stat.m_lectureTitle.isEmpty())
		stat.m_lectureTitle = m_lecture.m_title;
	return stat;
}
// ----------------------------------------------------------------------------

void KTouch::clearStatistics() {
	m_stats.clear();
}
// ----------------------------------------------------------------------------

// ********************
// *** Public slots ***
// ********************

void KTouch::applyPreferences() {
	// This applies a new color scheme for the keyboard and also updates all other
	// changes for the keyboard widget
	changeColor(Prefs::currentColorScheme());
	m_slideLineWidget->applyPreferences();
	m_statusWidget->applyPreferences();
}
// ----------------------------------------------------------------------------

void KTouch::keyPressEvent(TQKeyEvent *keyEvent) {
    if (keyEvent->text().isEmpty()) return;

    // if we the training session is paused, continue training now
    if (m_trainer->m_trainingPaused)  {
		m_trainingPause->setEnabled(true);
		m_trainer->continueTraining();
	}
	if (keyEvent->text().length() > 1) {
		kdDebug() << "[KTouch::keyPressEvent]  text = '" << keyEvent->text() << "'" << endl;
	}
    TQChar key = keyEvent->text().at(0); // get first unicode character
	// HACK : manually filter out known dead keys
//	bool has_dead_key = true;
	switch (key.unicode()) {
		case 94   : m_lastDeadKey = TQChar(uint(94)); break;
		case 176  : m_lastDeadKey = TQChar(uint(176)); break;
		case 180  : m_lastDeadKey = TQChar(uint(180)); break;
		case 96   : m_lastDeadKey = TQChar(uint(96)); break;
		case 126  : m_lastDeadKey = TQChar(uint(126)); break;
		default   : m_lastDeadKey = TQChar(uint(0));
	}
	if (m_lastDeadKey != TQChar(uint(0)) && key == m_lastDeadKey) {
//		kdDebug() << "Got dead key = " << m_lastDeadKey << endl;
    	//keyEvent->accept();
//		return;
	}

    if (key.isPrint())
        m_trainer->keyPressed(key);
    else if (key==TQChar(8))
        m_trainer->backspacePressed();
    else if (key==TQChar(13))
        m_trainer->enterPressed();
    else
        return; // unrecognised char -> don't accept it! Maybe the key is for somebody else?
    keyEvent->accept();
}
// ----------------------------------------------------------------------------


void KTouch::imEndEvent ( TQIMEvent * e ){
    m_trainer->keyPressed(e->text().at(0));
    e->accept();
}


void KTouch::configOverrideLectureFontToggled(bool on) {
	if (on) {
		m_pageGeneral->fontTextLabel->setEnabled(true);
		m_pageGeneral->kcfg_Font->setEnabled(true);
	}
	else {
		m_pageGeneral->fontTextLabel->setEnabled(false);
		m_pageGeneral->kcfg_Font->setEnabled(false);
	}	
}
// ----------------------------------------------------------------------------

void KTouch::configOverrideKeyboardFontToggled(bool on) {
	if (on) {
		m_pageKeyboard->textLabel1->setEnabled(true);
		m_pageKeyboard->kcfg_KeyboardFont->setEnabled(true);
	}
	else {
		m_pageKeyboard->textLabel1->setEnabled(false);
		m_pageKeyboard->kcfg_KeyboardFont->setEnabled(false);
	}	
}
// ----------------------------------------------------------------------------

void KTouch::configAutoLevelChangeToggled(bool on) {
	if (on) {
		m_pageTraining->l1->setEnabled(true);
		m_pageTraining->l2->setEnabled(true);
		m_pageTraining->l3->setEnabled(true);
		m_pageTraining->l4->setEnabled(true);
		m_pageTraining->l5->setEnabled(true);
		m_pageTraining->l6->setEnabled(true);
		m_pageTraining->l7->setEnabled(true);
		m_pageTraining->l8->setEnabled(true);
		m_pageTraining->l9->setEnabled(true);
		m_pageTraining->l10->setEnabled(true);
		m_pageTraining->kcfg_UpSpeedLimit->setEnabled(true);
		m_pageTraining->kcfg_UpCorrectLimit->setEnabled(true);
		m_pageTraining->kcfg_DownSpeedLimit->setEnabled(true);
		m_pageTraining->kcfg_DownCorrectLimit->setEnabled(true);
		m_pageTraining->kcfg_DisableManualLevelChange->setEnabled(true);
	}
	else {
		m_pageTraining->l1->setEnabled(false);
		m_pageTraining->l2->setEnabled(false);
		m_pageTraining->l3->setEnabled(false);
		m_pageTraining->l4->setEnabled(false);
		m_pageTraining->l5->setEnabled(false);
		m_pageTraining->l6->setEnabled(false);
		m_pageTraining->l7->setEnabled(false);
		m_pageTraining->l8->setEnabled(false);
		m_pageTraining->l9->setEnabled(false);
		m_pageTraining->l10->setEnabled(false);
		m_pageTraining->kcfg_UpSpeedLimit->setEnabled(false);
		m_pageTraining->kcfg_UpCorrectLimit->setEnabled(false);
		m_pageTraining->kcfg_DownSpeedLimit->setEnabled(false);
		m_pageTraining->kcfg_DownCorrectLimit->setEnabled(false);
		m_pageTraining->kcfg_DisableManualLevelChange->setEnabled(false);
	}	
}
// ----------------------------------------------------------------------------

void KTouch::configCommonColorsToggled(bool on) {
	m_pageColors->colorsGroup->setEnabled(on);
}
// ----------------------------------------------------------------------------

// The action File->Open lecture...
void KTouch::fileOpenLecture() {
	trainingPause();
	KURL tmp = KFileDialog::getOpenURL(TQString::null, TQString::null, this, i18n("Select Training Lecture File") );
    if (!tmp.isEmpty()) {
		// first store training statistics
		m_trainer->storeTrainingStatistics();
		Prefs::setCurrentLectureFile(tmp.url());
		m_lecture.loadXML(this, Prefs::currentLectureFile() );
		updateFontFromLecture();
		// adjust check marks in quick-select menus
		updateLectureActionCheck();
	}
	// restart training session from level 1 here...
	m_trainer->startTraining(false);
	m_trainingPause->setEnabled(true);
}
// ----------------------------------------------------------------------------

// The action File->Edit lecture...
void KTouch::fileEditLecture() {
	trainingPause();
	m_trainer->storeTrainingStatistics();
	// Create and execute editor
    KTouchLectureEditor dlg(this);
    dlg.startEditor( Prefs::currentLectureFile() );
	// Reload lecture in case it was modified
	m_lecture.loadXML(this, Prefs::currentLectureFile() );
	updateFontFromLecture();
	// adjust check marks in quick-select menus
	updateLectureActionCheck();
	// restart training session here (keep level)...
	m_trainer->startTraining(true);
	m_trainingPause->setEnabled(true);
}
// ----------------------------------------------------------------------------

// The action File->Edit colors...
void KTouch::fileEditColors() {
	trainingPause();
	// Create a copy of the currently editable color schemes.
	TQValueList<KTouchColorScheme> tmp_list;
	int default_schemes = 0;
	for (TQValueVector<KTouchColorScheme>::const_iterator it = KTouchColorScheme::m_colorSchemes.constBegin();
		it != KTouchColorScheme::m_colorSchemes.constEnd(); ++it)
	{
		if (!it->m_default) 	tmp_list.append(*it);
		else 					++default_schemes;
	}
	
	KTouchColorEditor dlg(this);		// Create editor
	// start editor
	int selected;
	dlg.startEditor( tmp_list, Prefs::currentColorScheme() - default_schemes, selected);
	KTouchColorScheme::createDefaults();
	for (TQValueList<KTouchColorScheme>::const_iterator it = tmp_list.constBegin();
		it != tmp_list.constEnd(); ++it)
	{
		KTouchColorScheme::m_colorSchemes.append(*it);
	}
	// update the quick select menu
    TQStringList schemes_list;
    for (unsigned int i=0; i<KTouchColorScheme::m_colorSchemes.count(); ++i)
		schemes_list.append(KTouchColorScheme::m_colorSchemes[i].m_name);
    m_keyboardColorAction->setItems(schemes_list);
	int index = selected + default_schemes;
	if (index >=0 && index < static_cast<int>(KTouchColorScheme::m_colorSchemes.count())) {
		Prefs::setCurrentColorScheme(index);
	}
	else {
		Prefs::setCurrentColorScheme(1); // fall back on default in case active was deleted
	}
   	m_keyboardColorAction->setCurrentItem(Prefs::currentColorScheme());
	applyPreferences();
}
// ----------------------------------------------------------------------------

// The action File->Edit keyboard...
void KTouch::fileEditKeyboard() {
	trainingPause();
	// Create and execute editor
//    KTouchKeyboardEditor dlg(this);
//    dlg.startEditor( Prefs::currentKeyboardFile() );
	// Reload lecture in case it was modified
	//m_keyboard.loadXML(this, Prefs::currentKeyboardFile() );
	//updateFontFromLecture();
	// adjust check marks in quick-select menus
	//updateKeyboardActionCheck();
}
// ----------------------------------------------------------------------------

void KTouch::fileQuit() {
    kapp->quit();
}
// ----------------------------------------------------------------------------

void KTouch::trainingNewSession() {
    trainingPause();
	int result = KMessageBox::questionYesNoCancel(this, 
		i18n("Would you like to keep the current level for the new training session?"),
		i18n("Start New Training Session"),i18n("Keep Current Level"),i18n("Do Not Keep"));
	if (result == KMessageBox::Cancel) return;
	// store the statistics obtained so far in the trainer object
	m_trainer->storeTrainingStatistics();
	// start new training session here
	m_trainer->startTraining(result == KMessageBox::Yes);
    m_trainingPause->setEnabled(true);
}
// ----------------------------------------------------------------------------

void KTouch::trainingPause() {
    m_trainingPause->setEnabled(false);
    m_trainer->pauseTraining();
}
// ----------------------------------------------------------------------------

void KTouch::trainingStatistics() {
    trainingPause();
	KTouchStatistics dlg(this);
	// TODO : this is somewhat messy: we have to get the words in the
    //        current line (since they are not stored in the current
    //        level and session stats, because the student may delete
    //        the whole line again) and add them manually to copies
    //        of the currents stats
	KTouchSessionStats kss = m_trainer->m_sessionStats;
	KTouchLevelStats kls = m_trainer->m_levelStats;
	kss.m_words += m_trainer->wordsInCurrentLine();
	kls.m_words += m_trainer->wordsInCurrentLine();
	// by calling getCurrentLectureStats we ensure that there is 
    // data for the current lecture present for the dialog to function
	// properly
	getCurrentLectureStats();
    dlg.run(Prefs::currentLectureFile(), m_stats, kls, kss);
}
// ----------------------------------------------------------------------------

void KTouch::optionsPreferences() {
    trainingPause();
	if ( KConfigDialog::showDialog( "settings" ) )	return;
	// KConfigDialog didn't find an instance of this dialog, so lets create it :
	KConfigDialog* dialog = new KConfigDialog( this, "settings",  Prefs::self() );
	m_pageGeneral = new KTouchPrefGeneralLayout(0, "General");
	dialog->addPage(m_pageGeneral, i18n("General Options"), "style");
	m_pageTraining = new KTouchPrefTrainingLayout(0, "Training");
	dialog->addPage(m_pageTraining, i18n("Training Options"), "kalarm");
	m_pageKeyboard = new KTouchPrefKeyboardLayout(0, "Keyboard");
	dialog->addPage(m_pageKeyboard, i18n("Keyboard Settings"), "keyboard_layout");
	m_pageColors = new KTouchPrefColorsLayout(0, "Colors"); 
	dialog->addPage(m_pageColors, i18n("Color Settings"), "package_graphics");
	connect(dialog, TQT_SIGNAL(settingsChanged()), this, TQT_SLOT(applyPreferences()));
	// TODO : Connect some other buttons/check boxes of the dialog
	connect(m_pageGeneral->kcfg_OverrideLectureFont, TQT_SIGNAL(toggled(bool)), 
		this, TQT_SLOT(configOverrideLectureFontToggled(bool)));
	connect(m_pageKeyboard->kcfg_OverrideKeyboardFont, TQT_SIGNAL(toggled(bool)), 
		this, TQT_SLOT(configOverrideKeyboardFontToggled(bool)));
	connect(m_pageTraining->kcfg_AutoLevelChange, TQT_SIGNAL(toggled(bool)), 
		this, TQT_SLOT(configAutoLevelChangeToggled(bool)));
	connect(m_pageColors->kcfg_CommonTypingLineColors, TQT_SIGNAL(toggled(bool)),
		this, TQT_SLOT(configCommonColorsToggled(bool)));
	// call the functions to enable/disable controls depending on settings
	configOverrideLectureFontToggled(Prefs::overrideLectureFont());
	configOverrideKeyboardFontToggled(Prefs::overrideKeyboardFont());
	configAutoLevelChangeToggled(Prefs::autoLevelChange());
	configCommonColorsToggled(Prefs::commonTypingLineColors());
	dialog->show();
}
// ----------------------------------------------------------------------------

void KTouch::changeStatusbarMessage(const TQString& text) {
    statusBar()->message(text);
}
// ----------------------------------------------------------------------------

void KTouch::changeStatusbarStats(unsigned int level_correct, unsigned int level_total, unsigned int level_words,
	unsigned int session_correct, unsigned int session_total, unsigned int session_words) 
{
	statusBar()->changeItem(i18n( "Level:  Correct/Total chars: %1/%2  Words: %3")
			.arg(level_correct).arg(level_total).arg(level_words), 1);
	statusBar()->changeItem(i18n( "Session: Correct/Total chars: %1/%2  Words: %3")
			.arg(session_correct).arg(session_total).arg(session_words), 2);
}
// ----------------------------------------------------------------------------

void KTouch::changeKeyboard(int num) {
    if (static_cast<unsigned int>(num)>=m_keyboardFiles.count()) return;
    Prefs::setCurrentKeyboardFile( m_keyboardFiles[num] );
//	kdDebug() << "[KTouch::changeKeyboard]  new keyboard layout = " << Prefs::currentKeyboardFile() << endl;
    m_keyboardLayoutAction->setCurrentItem(num);
	// call Apply-Preferenzes in "noisy"-mode, pop up an error if the chosen layout file is corrupt
    m_keyboardWidget->applyPreferences(this, false);
}
// ----------------------------------------------------------------------------

void KTouch::changeColor(int num) {
    if (static_cast<unsigned int>(num)>=KTouchColorScheme::m_colorSchemes.count()) return;
    Prefs::setCurrentColorScheme(num);
    m_keyboardWidget->applyPreferences(this, false);
	m_slideLineWidget->applyPreferences();
}
// ----------------------------------------------------------------------------

void KTouch::changeLecture(int num) {
    if (static_cast<unsigned int>(num)>=m_lectureFiles.count()) return;
    trainingPause();
	KTouchLecture l;
	TQString fileName = m_lectureFiles[num];
    if (!l.loadXML(this, KURL::fromPathOrURL(fileName))) {
        KMessageBox::sorry(0, i18n("Could not find/open the lecture file '%1'.").arg(fileName) );
    	m_defaultLectureAction->setCurrentItem(-1);
	}
	else {
		// store the statistics obtained so far in the trainer object
		m_trainer->storeTrainingStatistics();
		// set new lecture as current
		Prefs::setCurrentLectureFile( fileName );
		m_lecture = l;
		updateFontFromLecture();
    	m_defaultLectureAction->setCurrentItem(num);
    	// now let's start the training in the first level of the training lecture
		m_trainer->startTraining(false);
		m_trainingPause->setEnabled(true);
	}
}
// ----------------------------------------------------------------------------


// *********************************
// *** Protected member function ***
// *********************************

bool KTouch::queryExit() {
	// store config data
	Prefs::setCurrentTrainingLevel( m_trainer->m_level );
    Prefs::writeConfig();
	// update and save statistics
	m_trainer->storeTrainingStatistics();
	KURL stat_file = KGlobal::dirs()->saveLocation("data","ktouch", true) + "statistics.xml";
	//kdDebug() << "[KTouch::queryExit]  Writing statistics to file: '" << stat_file << "'" << endl;
	m_stats.write(this, stat_file);
	KURL color_file = KGlobal::dirs()->saveLocation("data","ktouch", true) + "color_schemes.xml";
	KTouchColorScheme::writeList(this, color_file);
    return true;
}
// ----------------------------------------------------------------------------

void KTouch::resizeEvent(TQResizeEvent * event) {
	changeStatusbarStats(m_trainer->m_levelStats.m_correctChars, m_trainer->m_levelStats.m_totalChars, m_trainer->m_levelStats.m_words,
		m_trainer->m_sessionStats.m_correctChars, m_trainer->m_sessionStats.m_totalChars, m_trainer->m_sessionStats.m_words);
	KMainWindow::resizeEvent(event);
}
// ----------------------------------------------------------------------------


// *******************************
// *** Private member function ***
// *******************************

// Will be called when this app is restored due to session management.
// This function only stored the temperary data of the last session. All permanent
// settings should be handled by the KTouchConfiguration object.
void KTouch::readProperties(KConfig *config) {
    kdDebug() << "[KTouch::readProperties]  Reading session data..." << endl;
	// TODO : Session management rewrite
    config->setGroup("TrainingState");
	
/*
	// The application is about to be restored due to session management.
    // Let's read all the stuff that was set when the application was terminated (during KDE logout).
    TQString session = config->readEntry("Session");
    if (!session.isEmpty())
        m_trainer->m_session = KTouchTrainingSession(session);
    m_trainer->m_level = config->readNumEntry("Level", 0);
    m_trainer->m_line = config->readNumEntry("Line", 0);
    m_currentLectureFile = config->readPathEntry("Lecture");
    m_trainer->readSessionHistory();    // read session history (excluding currently active session)
    // update the trainer object
    m_trainer->m_teacherText = m_lecture.level(m_trainer->m_level).line(m_trainer->m_line);
    m_trainer->m_studentText = config->readEntry("StudentText");
    m_trainer->continueTraining();
    changeStatusbarMessage( i18n("Restarting training session: Waiting for first keypress...") );
    // update the slide line widget
    m_slideLineWidget->setNewText(m_trainer->m_teacherText, m_trainer->m_studentText);
    // update all the other widgets
    m_trainer->updateWidgets();
	// Read training state
    config->setGroup("TrainingState");
    m_currentLectureURL = config->readPathEntry("LectureURL");
    m_trainer->m_level = config->readNumEntry("Level", 0);
*/
}
// ----------------------------------------------------------------------------

// Will be called when the app should save its state for session management purposes.
void KTouch::saveProperties(KConfig *config) {
    kdDebug() << "[KTouch::saveProperties]  Saving session data..." << endl;
    // We are going down because of session management (most likely because of
    // KDE logout). Let's save the current status so that we can restore it
    // next logon.

	// TODO : Session management rewrite
/*
    config->setGroup("TrainingState");
    // first write the current lecture URL and the training position
    config->writePathEntry("Lecture", m_currentLectureURL.url());
    config->writeEntry("Level", m_trainer->m_level);
    config->writeEntry("Line", m_trainer->m_line);
    config->writeEntry("StudentText", m_trainer->m_studentText);
    config->writeEntry("Session", m_trainer->m_session.asString() );
	// store the session history so far
    m_trainer->writeSessionHistory();
    
	config->setGroup("TrainingState");
    config->writePathEntry("LectureURL", m_currentLectureURL.url());
    config->writeEntry("Level", m_trainer->m_level);
    // during normal shutdown we finish the session and add it to the session history
    m_trainer->m_sessionHistory.append( m_trainer->m_session );
    m_trainer->writeSessionHistory();
*/
}
// ----------------------------------------------------------------------------

// Initialises the program during a normal startup
void KTouch::init() {
	//kdDebug() << "[KTouch::init]  populating file lists..." << endl;
    updateFileLists();  // create lists with default lecture/keyboard/examination files/colour scheme files
	//kdDebug() << "[KTouch::init]  " << m_lectureFiles.count() << " lectures available" << endl;
	//kdDebug() << "[KTouch::init]  " << m_keyboardFiles.count() << " keyboard layouts available" << endl;
	//kdDebug() << "[KTouch::init]  " << m_examinationFiles.count() << " examination files available" << endl;

	if (Prefs::currentLectureFile() == "default") {
		Prefs::setCurrentLectureFile(TQString::null);
//		/// \todo look up a lecture in the language of the KDE locale
/*		TQString default_lecture = "default";
		if (m_lectureFiles.count() > 0)  default_lecture = m_lectureFiles[0];
		Prefs::setCurrentLectureFile( default_lecture );
*/
	}

    // if keyboard layout (loaded by Prefs is not available (e.g. the 
    // layout file has been deleted) switch to default keyboard
    if (m_keyboardFiles.contains(Prefs::currentKeyboardFile() )==0) {
        TQString default_keyboard;
        // determine locale
        TQString lang = KGlobal::locale()->language();
        TQString fname = lang + ".keyboard";
        // try to find keyboard with current locale
        TQStringList::const_iterator it = m_keyboardFiles.constBegin();
        while (it != m_keyboardFiles.constEnd()   &&   (*it).find(fname) == -1)  ++it;
        if (it == m_keyboardFiles.constEnd()) {
            fname = lang.left(2) + ".keyboard";
            // try to find more general version 
            it = m_keyboardFiles.constBegin();
            while (it != m_keyboardFiles.constEnd()   &&   (*it).find(fname) == -1)  ++it;
        }

        if (it != m_keyboardFiles.constEnd())
            default_keyboard = *it;
        else 
            default_keyboard = "number.keyboard";
        Prefs::setCurrentKeyboardFile ( default_keyboard );
    }

    // create some default colour schemes
    KTouchColorScheme::createDefaults();
    // read additional color schemes
	KURL color_file = KGlobal::dirs()->findResource("data", "ktouch/color_schemes.xml");
	KTouchColorScheme::readList(this, color_file);
}
// ----------------------------------------------------------------------------

// Creates the layout and GUI setup for a practice session
void KTouch::initTrainingSession() {
	//kdDebug() << "[KTouch::initTrainingSession]  setting up layouts and widgets for new training session..." << endl;
    // Build the training area. The status widget has a fixed vertical size, the slide line and the
    // keyboard grow according to their vertical stretch factors (see last argument in the constructors
    // of TQSizePolicy)
    TQVBox * mainLayout = new TQVBox( this );
    m_statusWidget = new KTouchStatus( mainLayout );
    m_slideLineWidget = new KTouchSlideLine( mainLayout );
    m_slideLineWidget->setSizePolicy( TQSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 0, 1) );
    m_keyboardWidget = new KTouchKeyboardWidget( mainLayout );
    m_keyboardWidget->setSizePolicy(TQSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 0, 3) );
    setCentralWidget(mainLayout);
	// apply the settings to the widgets
    m_slideLineWidget->applyPreferences();
    m_keyboardWidget->applyPreferences(this, true);  // set preferences silently here

    // create our trainer, the master object for the training stuff...
	if (m_trainer != NULL)  delete m_trainer;
    m_trainer = new KTouchTrainer(m_statusWidget, m_slideLineWidget, m_keyboardWidget, &m_lecture);

    // Setup status bar
    statusBar()->show();
	statusBar()->insertItem("Level", 1, 0, true);
	statusBar()->insertItem("Session", 2, 0, true);
}
// ----------------------------------------------------------------------------

// Creates the (standard) actions and entries in the menu.
void KTouch::setupActions() {
	// *** File menu ***
    new KAction(i18n("&Open lecture..."), "open_lecture", 0,
		this, TQT_SLOT(fileOpenLecture()), actionCollection(), "file_openlecture");
    new KAction(i18n("&Edit lecture..."), "edit_lecture", 0,
		this, TQT_SLOT(fileEditLecture()), actionCollection(), "file_editlecture");
    new KAction(i18n("&Edit color scheme..."), "edit_colors", 0,
		this, TQT_SLOT(fileEditColors()), actionCollection(), "file_editcolors");
//    new KAction(i18n("&Edit Keyboard..."), "edit_keyboard", 0, 
//		this, TQT_SLOT(fileEditKeyboard()), actionCollection(), "file_editkeyboard");
    KStdAction::quit(this, TQT_SLOT(fileQuit()), actionCollection());

	// *** Training menu ***
    new KAction(i18n("&Start New Session"), "launch", 0,
        this, TQT_SLOT(trainingNewSession()), actionCollection(), "training_newsession");
    m_trainingPause = new KAction(i18n("&Pause Session"), "player_pause", 0,
        this, TQT_SLOT(trainingPause()), actionCollection(), "training_pause");
    new KAction(i18n("&Lecture Statistics"), "kalarm", 0,
        this, TQT_SLOT(trainingStatistics()), actionCollection(), "training_stats");
 
    // Setup menu entries for the training lectures
    m_defaultLectureAction = new KSelectAction(i18n("Default &Lectures"), 0, this, 0, actionCollection(), "default_lectures");
	m_defaultLectureAction->setMenuAccelsEnabled(false);
    m_defaultLectureAction->setItems(m_lectureTitles);
    m_defaultLectureAction->setCurrentItem(0);  
    connect (m_defaultLectureAction, TQT_SIGNAL(activated(int)), this, TQT_SLOT(changeLecture(int)));

	// *** Settings menu ***    
    KStdAction::preferences(this, TQT_SLOT(optionsPreferences()), actionCollection());
    // Setup menu entries for keyboard layouts    
    m_keyboardLayoutAction= new KSelectAction(i18n("&Keyboard Layouts"), 0, this, 0, actionCollection(), "keyboard_layouts");
	m_keyboardLayoutAction->setMenuAccelsEnabled(false);
    m_keyboardLayoutAction->setItems(m_keyboardTitles);
    connect (m_keyboardLayoutAction, TQT_SIGNAL(activated(int)), this, TQT_SLOT(changeKeyboard(int)));

	// Setup menu entries for colour schemes
	m_keyboardColorAction = new KSelectAction(i18n("&Color Schemes"), 0, this, 0, actionCollection(), "keyboard_schemes");
    TQStringList schemes_list;
    for (unsigned int i=0; i<KTouchColorScheme::m_colorSchemes.count(); ++i)
		schemes_list.append(KTouchColorScheme::m_colorSchemes[i].m_name);
	m_keyboardColorAction->setMenuAccelsEnabled(false);
    m_keyboardColorAction->setItems(schemes_list);
	if (static_cast<unsigned int>(Prefs::currentColorScheme()) >=  schemes_list.count())
		Prefs::setCurrentColorScheme(1);
   	m_keyboardColorAction->setCurrentItem(Prefs::currentColorScheme());
    connect (m_keyboardColorAction, TQT_SIGNAL(activated(int)), this, TQT_SLOT(changeColor(int)));
}
// ----------------------------------------------------------------------------

// This function updates the font used in the sliding line of a font suggestions was
// made for the current lecture.
void KTouch::updateFontFromLecture() {
	// if the lecture requires a font, try this 
	if (!m_lecture.m_fontSuggestions.isEmpty()) {
		TQFont f;
		// TODO : if multiple font suggestions are given, try one after another until a
		// suggested font is found
		if (f.fromString(m_lecture.m_fontSuggestions))	m_slideLineWidget->setFont(f);
		else if (f.fromString("Monospace")) 		m_slideLineWidget->setFont(f);
	}
}
// ----------------------------------------------------------------------------

// This function populates the file lists with the installed training, keyboard and 
// examination files.
void KTouch::updateFileLists() {
    KStandardDirs *dirs = KGlobal::dirs();
	
    // first search for all installed keyboard files
	// TODO : search in i18n() directories
    m_keyboardFiles = dirs->findAllResources("data","ktouch/*.keyboard");

    // remove the number layout, since this is the necessary default layout and will be
    // added anyway
    TQStringList::iterator it = m_keyboardFiles.find("number.keyboard");
    if (it!=m_keyboardFiles.end())      m_keyboardFiles.remove(it);

    m_keyboardTitles.clear();
    for (TQStringList::const_iterator cit = m_keyboardFiles.constBegin();
        cit != m_keyboardFiles.constEnd(); ++cit)
    {
        // extract titles from keyboard files and store them in the
        // m_keyboardTitles string list

        // get the filename alone
        TQString fname = KURL(*cit).fileName();
        // get the filename without the .keyboard
        fname.truncate(fname.length() - 9);
        // get everything in front of the first .
        TQString lang_iso = fname.section('.',0,0);
        // get language description of file names
        TQString lang_name = KGlobal::locale()->twoAlphaToLanguageName(lang_iso);
//        kdDebug() << fname << " | " << lang_iso << " | " << lang_name << endl;
        if (lang_name.isEmpty())
            lang_name = KGlobal::locale()->twoAlphaToCountryName(lang_iso);
        if (!lang_name.isEmpty())
            lang_name += " (" + fname + ")";
        else
            lang_name = fname;
        m_keyboardTitles.append( lang_name );
//        kdDebug() << m_keyboardTitles.back() << endl;
    }

    // now sort the files and titles accordingly
    sort_lists(m_keyboardTitles, m_keyboardFiles);
    // and add the number keypad to the front
    m_keyboardFiles.push_front("number.keyboard");
    m_keyboardTitles.push_front(i18n("Keypad/Number block"));

    // Now lets find the lecture files.
	// TODO : search in i18n() directories
    TQStringList lectureFiles = dirs->findAllResources("data","ktouch/*.ktouch.xml");
	// Now extract the titles of the lecture files and populate the string lists used in the program
	m_lectureFiles.clear();
	m_lectureTitles.clear();
    if (!lectureFiles.isEmpty()) {
        // extract the prefixes
        for (TQStringList::iterator it=lectureFiles.begin(); it!=lectureFiles.end(); ++it) {
            KURL url(*it);
			KTouchLecture l;
			// only add lecture if we can actually load it
			if (l.loadXML(this, url)) {
				// since we could read the lecture, we remember the URL
				m_lectureFiles.push_back(*it);	
				// store the title of the lecture
				if (l.m_title.isEmpty())
					m_lectureTitles.push_back(i18n("untitled lecture") + " - (" + url.fileName() + ")");
            	else
					m_lectureTitles.push_back(l.m_title);
			}
        }
        sort_lists(m_lectureTitles, m_lectureFiles);
    }

	// Now find predefined files with colour schemes
    TQStringList colour_schemes = dirs->findAllResources("data","ktouch/*.colour_scheme");
	// TODO : read in colour schemes and populate TQValueList<KTouchColorScheme>
}
// ----------------------------------------------------------------------------


void KTouch::updateLectureActionCheck() {
	int num = 0;
	TQStringList::iterator it = m_lectureFiles.begin();
	TQString fname = Prefs::currentLectureFile(); 
	while (it != m_lectureFiles.end()   &&   (*it).find(fname) == -1) {
		++it;
		++num;
	}
	if (it == m_lectureFiles.end())		m_defaultLectureAction->setCurrentItem(-1);
	else    							m_defaultLectureAction->setCurrentItem(num);
}
// ----------------------------------------------------------------------------

void KTouch::updateKeyboardActionCheck() {
	int num = 0;
	TQStringList::iterator it = m_keyboardFiles.begin();
	TQString fname = Prefs::currentKeyboardFile(); 
	while (it != m_keyboardFiles.end()   &&   (*it).find(fname) == -1) {
		++it;
		++num;
	}
	if (it == m_keyboardFiles.end())	m_keyboardLayoutAction->setCurrentItem(-1);
	else					    		m_keyboardLayoutAction->setCurrentItem(num);
}
// ----------------------------------------------------------------------------

/*
void KTouch::imStartEvent(TQIMEvent *e) {
	kdDebug() << "[KTouch::imStartEvent]  text = '" << e->text() << "'" << endl;
  e->accept();
}
// ----------------------------------------------------------------------------

void KTouch::imComposeEvent(TQIMEvent *e) {
	kdDebug() << "[KTouch::imComposeEvent]  text = '" << e->text() << "'" << endl;
  e->accept();
}
// ----------------------------------------------------------------------------

void KTouch::imEndEvent(TQIMEvent *e) {
	kdDebug() << "[KTouch::imEndEvent]  text = '" << e->text() << "'" << endl;
  if (!e->text().isEmpty()) {
    if (e->text() == "^") {
      TQKeyEvent *ev = new TQKeyEvent (TQEvent::KeyPress,
                                    Qt::Key_AsciiCircum, '^', 0,
                                    TQString("^"));
      keyPressEvent(ev);
      delete ev;
    }
  }
  e->accept();
}
// ----------------------------------------------------------------------------
*/