diff options
Diffstat (limited to 'ktouch/src')
57 files changed, 13935 insertions, 0 deletions
diff --git a/ktouch/src/Makefile.am b/ktouch/src/Makefile.am new file mode 100644 index 00000000..2eda3b2e --- /dev/null +++ b/ktouch/src/Makefile.am @@ -0,0 +1,62 @@ +# this 10 paths are KDE specific. Use them: +# kde_htmldir Where your docs should go to. (contains lang subdirs) +# kde_appsdir Where your application file (.kdelnk) should go to. +# kde_icondir Where your icon should go to. +# kde_sounddir Where system sounds should go to. +# kde_datadir Where you install application data. (Use a subdir) +# kde_locale Where translation files should go to.(contains lang subdirs) +# kde_cgidir Where cgi-bin executables should go to. +# kde_confdir Where config files should go to. +# kde_mimedir Where mimetypes should go to. +# kde_toolbardir Where general toolbar icons should go to. +# kde_wallpaperdir Where general wallpapers should go to. + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) -I$(top_srcdir) + +# claim, which subdirectories you want to install +# if you don't have subdirectories, comment it +# SUBDIRS = + +# these are the headers for your project +noinst_HEADERS = ktouchchartwidget.h ktouchcolorscheme.h ktouchdefaults.h \ + ktouch.h ktouchkeyboardeditor.h ktouchkeyboard.h ktouchkeyboardwidget.h \ + ktouchkeyconnector.h ktouchkey.h ktouchkeys.h ktouchlectureeditor.h ktouchlecture.h \ + ktouchleveldata.h ktouchopenrequest.h ktouchslideline.h ktouchstatisticsdata.h \ + ktouchstatistics.h ktouchstatus.h ktouchtrainer.h ktouchutils.h ktouchcoloreditor.h + +# let automoc handle all of the meta source files (moc) +ktouch_METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/ktouch.pot + + +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = ktouch + +# the application source, library search path, and link libraries +ktouch_SOURCES = ktouchchartwidget.cpp ktouchcoloreditor_dlg.ui \ + ktouchcolorscheme.cpp ktouch.cpp ktouchdefaults.cpp ktouchkeyboardwidget.cpp \ + ktouchkeyconnector.cpp ktouchkey.cpp ktouchkeys.cpp ktouchlecture.cpp ktouchlectureeditor.cpp \ + ktouchlectureeditor_dlg.ui ktouchleveldata.cpp ktouchopenrequest.cpp ktouchopenrequest_dlg.ui \ + ktouchprefcolorslayout.ui ktouchprefgenerallayout.ui ktouchprefkeyboardlayout.ui \ + ktouchpreftraininglayout.ui ktouchslideline.cpp ktouchstatistics.cpp ktouchstatisticsdata.cpp \ + ktouchstatistics_dlg.ui ktouchstatus.cpp ktouchstatuslayout.ui ktouchtrainer.cpp \ + ktouchutils.cpp main.cpp prefs.kcfgc ktouchcoloreditor.cpp + +# the library search path. +ktouch_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +# the libraries to link against. Be aware of the order. First the libraries, +# that depend on the following ones. +ktouch_LDADD = ../../libkdeedu/kdeeduplot/libkdeeduplot.la $(LIB_KIO) + +rcdir = $(kde_datadir)/ktouch +rc_DATA = ktouchui.rc +kde_kcfg_DATA = ktouch.kcfg + diff --git a/ktouch/src/ktouch.cpp b/ktouch/src/ktouch.cpp new file mode 100644 index 00000000..231bc402 --- /dev/null +++ b/ktouch/src/ktouch.cpp @@ -0,0 +1,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 <qvbox.h> +#include <qsignalmapper.h> +#include <qcheckbox.h> +#include <qlabel.h> +#include <qgroupbox.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( QSize(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(QString::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(QKeyEvent *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; + } + QChar 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 = QChar(uint(94)); break; + case 176 : m_lastDeadKey = QChar(uint(176)); break; + case 180 : m_lastDeadKey = QChar(uint(180)); break; + case 96 : m_lastDeadKey = QChar(uint(96)); break; + case 126 : m_lastDeadKey = QChar(uint(126)); break; + default : m_lastDeadKey = QChar(uint(0)); + } + if (m_lastDeadKey != QChar(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==QChar(8)) + m_trainer->backspacePressed(); + else if (key==QChar(13)) + m_trainer->enterPressed(); + else + return; // unrecognised char -> don't accept it! Maybe the key is for somebody else? + keyEvent->accept(); +} +// ---------------------------------------------------------------------------- + + +void KTouch::imEndEvent ( QIMEvent * 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(QString::null, QString::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. + QValueList<KTouchColorScheme> tmp_list; + int default_schemes = 0; + for (QValueVector<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 (QValueList<KTouchColorScheme>::const_iterator it = tmp_list.constBegin(); + it != tmp_list.constEnd(); ++it) + { + KTouchColorScheme::m_colorSchemes.append(*it); + } + // update the quick select menu + QStringList 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, SIGNAL(settingsChanged()), this, SLOT(applyPreferences())); + // TODO : Connect some other buttons/check boxes of the dialog + connect(m_pageGeneral->kcfg_OverrideLectureFont, SIGNAL(toggled(bool)), + this, SLOT(configOverrideLectureFontToggled(bool))); + connect(m_pageKeyboard->kcfg_OverrideKeyboardFont, SIGNAL(toggled(bool)), + this, SLOT(configOverrideKeyboardFontToggled(bool))); + connect(m_pageTraining->kcfg_AutoLevelChange, SIGNAL(toggled(bool)), + this, SLOT(configAutoLevelChangeToggled(bool))); + connect(m_pageColors->kcfg_CommonTypingLineColors, SIGNAL(toggled(bool)), + this, 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 QString& 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; + QString 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(QResizeEvent * 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). + QString 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(QString::null); +// /// \todo look up a lecture in the language of the KDE locale +/* QString 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) { + QString default_keyboard; + // determine locale + QString lang = KGlobal::locale()->language(); + QString fname = lang + ".keyboard"; + // try to find keyboard with current locale + QStringList::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 QSizePolicy) + QVBox * mainLayout = new QVBox( this ); + m_statusWidget = new KTouchStatus( mainLayout ); + m_slideLineWidget = new KTouchSlideLine( mainLayout ); + m_slideLineWidget->setSizePolicy( QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding, 0, 1) ); + m_keyboardWidget = new KTouchKeyboardWidget( mainLayout ); + m_keyboardWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::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, SLOT(fileOpenLecture()), actionCollection(), "file_openlecture"); + new KAction(i18n("&Edit lecture..."), "edit_lecture", 0, + this, SLOT(fileEditLecture()), actionCollection(), "file_editlecture"); + new KAction(i18n("&Edit color scheme..."), "edit_colors", 0, + this, SLOT(fileEditColors()), actionCollection(), "file_editcolors"); +// new KAction(i18n("&Edit Keyboard..."), "edit_keyboard", 0, +// this, SLOT(fileEditKeyboard()), actionCollection(), "file_editkeyboard"); + KStdAction::quit(this, SLOT(fileQuit()), actionCollection()); + + // *** Training menu *** + new KAction(i18n("&Start New Session"), "launch", 0, + this, SLOT(trainingNewSession()), actionCollection(), "training_newsession"); + m_trainingPause = new KAction(i18n("&Pause Session"), "player_pause", 0, + this, SLOT(trainingPause()), actionCollection(), "training_pause"); + new KAction(i18n("&Lecture Statistics"), "kalarm", 0, + this, 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, SIGNAL(activated(int)), this, SLOT(changeLecture(int))); + + // *** Settings menu *** + KStdAction::preferences(this, 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, SIGNAL(activated(int)), this, SLOT(changeKeyboard(int))); + + // Setup menu entries for colour schemes + m_keyboardColorAction = new KSelectAction(i18n("&Color Schemes"), 0, this, 0, actionCollection(), "keyboard_schemes"); + QStringList 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, SIGNAL(activated(int)), this, 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()) { + QFont 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 + QStringList::iterator it = m_keyboardFiles.find("number.keyboard"); + if (it!=m_keyboardFiles.end()) m_keyboardFiles.remove(it); + + m_keyboardTitles.clear(); + for (QStringList::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 + QString fname = KURL(*cit).fileName(); + // get the filename without the .keyboard + fname.truncate(fname.length() - 9); + // get everything in front of the first . + QString lang_iso = fname.section('.',0,0); + // get language description of file names + QString 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 + QStringList 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 (QStringList::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 + QStringList colour_schemes = dirs->findAllResources("data","ktouch/*.colour_scheme"); + // TODO : read in colour schemes and populate QValueList<KTouchColorScheme> +} +// ---------------------------------------------------------------------------- + + +void KTouch::updateLectureActionCheck() { + int num = 0; + QStringList::iterator it = m_lectureFiles.begin(); + QString 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; + QStringList::iterator it = m_keyboardFiles.begin(); + QString 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(QIMEvent *e) { + kdDebug() << "[KTouch::imStartEvent] text = '" << e->text() << "'" << endl; + e->accept(); +} +// ---------------------------------------------------------------------------- + +void KTouch::imComposeEvent(QIMEvent *e) { + kdDebug() << "[KTouch::imComposeEvent] text = '" << e->text() << "'" << endl; + e->accept(); +} +// ---------------------------------------------------------------------------- + +void KTouch::imEndEvent(QIMEvent *e) { + kdDebug() << "[KTouch::imEndEvent] text = '" << e->text() << "'" << endl; + if (!e->text().isEmpty()) { + if (e->text() == "^") { + QKeyEvent *ev = new QKeyEvent (QEvent::KeyPress, + Qt::Key_AsciiCircum, '^', 0, + QString("^")); + keyPressEvent(ev); + delete ev; + } + } + e->accept(); +} +// ---------------------------------------------------------------------------- +*/ diff --git a/ktouch/src/ktouch.h b/ktouch/src/ktouch.h new file mode 100644 index 00000000..9ebdcfc8 --- /dev/null +++ b/ktouch/src/ktouch.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * ktouch.h * + * -------- * + * 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. * + ***************************************************************************/ + +#ifndef KTOUCH_H +#define KTOUCH_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qcolor.h> +#include <qstringlist.h> +#include <qvaluevector.h> + +#include <kmainwindow.h> +#include <kapplication.h> +#include <kurl.h> + +class QLabel; +class KToggleAction; +class KActionMenu; +class KSelectAction; + +class KTouchStatus; +class KTouchSlideLine; +class KTouchKeyboardWidget; +class KTouchTrainer; + +class KTouchPrefTrainingLayout; +class KTouchPrefKeyboardLayout; +class KTouchPrefGeneralLayout; +class KTouchPrefColorsLayout; + +#include "ktouchlecture.h" +#include "ktouchstatisticsdata.h" + +/// This is the main window of KTouch. +/// +/// It handles the lecture, training data, status and all other widgets that are +/// needed to get the program running. Since all special tasks are delegated to the +/// appropriate widgets and classes, the remaining code in KTouch is basically the +/// startup and KAction stuff.<p> +/// A word about dialogs. All dialogs in this program are not created by default. +/// Instead they are created "on first use". This +/// saves memory (because we don't need them always) and the startup speed increases.<p> +/// One central function - the keyPressEvent() - is responsable for getting the actual +/// typed char. It delegates the character to the trainer +/// (KTouchTrainer), which will then process it. So the heavy work lies in the trainer +/// object and all the widgets. +class KTouch : public KMainWindow { + Q_OBJECT + public: + /// Constructor, creates the KTouch proggy. + KTouch(); + /// Destructor, releases memory of KTouch trainer. + ~KTouch(); + + /// Returns the available lecture files + const QStringList& lectureFiles() const { return m_lectureFiles; } + /// Returns the statistics object for the current lecture (as reference) + KTouchLectureStats& getCurrentLectureStats(); + /// Clears the statistics data. + void clearStatistics(); + /// Updates the status bar text. + void changeStatusbarMessage(const QString& text); + /// Updates the status bar statistics. + void 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); + + public slots: + /// Will be called when the "Apply"-button has been pressed in the preferences + /// dialog or when the user accepted the changes using the "OK"-button. + void applyPreferences(); + + /// Called from the configuration dialog. + void configOverrideLectureFontToggled(bool on); + /// Called from the configuration dialog. + void configOverrideKeyboardFontToggled(bool on); + /// Called from the configuration dialog. + void configAutoLevelChangeToggled(bool on); + /// Called from the configuration dialog. + void configCommonColorsToggled(bool on); + + void fileOpenLecture(); ///< The action File->Open lecture... + void fileEditLecture(); ///< The action File->Edit lecture... + void fileEditColors(); ///< The action File->Edit colors... + void fileEditKeyboard(); ///< The action File->Edit keyboard... + void fileQuit(); ///< The action File->Quit + void trainingNewSession(); ///< The action Training->Start new training session... + void trainingPause(); ///< The action Training->Pause training + void trainingStatistics(); ///< The action Training->Show training statistics... + void optionsPreferences(); ///< The action Settings->Configure KTouch... + + /// Quick-changes the keyboard layout (called from menu). + void changeKeyboard(int num); + /// Quick-changes the colour scheme used on the keyboard (called from menu). + void changeColor(int num); + /// Quick-changes the current training lecture file (called from menu). + void changeLecture(int num); + + protected: + /// Reimplementated to save preferences and + bool queryExit(); + /// Some layout fixes here... + void resizeEvent(QResizeEvent *); + /// Accepts a typed char. + void keyPressEvent(QKeyEvent *keyEvent); + + void imEndEvent (QIMEvent *e); + + private: + // *** BEGIN - Session management *** + /// Will be called when this app is restored due to session management. + void readProperties(KConfig *config); + /// Will be called when the app should save its state for session management purposes. + void saveProperties(KConfig *config); + // *** END - Session management *** + + /// Initialises the program during a normal startup + void init(); + /// Creates the layout and GUI setup for a practice session + void initTrainingSession(); + /// Creates the (standard) actions and entries in the menu. + void setupActions(); + /// This function updates the font used in the sliding line of a font suggestions was + /// made for the current lecture. + /// Call this function whenever you have read a new lecture file to update + /// the slide line widget. + void updateFontFromLecture(); + /// This function populates the file lists with the installed training, keyboard and + /// examination files. + void updateFileLists(); + /// Updates the check mark in the lecture-quick-selection menu depending on the + /// lecture in Prefs::currentLectureFile(). + void updateLectureActionCheck(); + /// Updates the check mark in the keyboard-quick-selection menu depending on the + /// lecture in Prefs::currentKeyboardFile(). + void updateKeyboardActionCheck(); + + // *** Public member variables *** + KAction *m_trainingPause; ///< Action for "pause training session". + + KSelectAction *m_keyboardLayoutAction; + KSelectAction *m_keyboardColorAction; + KSelectAction *m_defaultLectureAction; + + KTouchStatus *m_statusWidget; ///< Pointer to the status widget on top of the main widget. + KTouchSlideLine *m_slideLineWidget; ///< Pointer to the sliding line widget. + KTouchKeyboardWidget *m_keyboardWidget; ///< Pointer to the keyboard widget. + KTouchTrainer *m_trainer; ///< The training 'master' (runs the training). + KTouchLecture m_lecture; ///< The lecture data. + + KTouchPrefGeneralLayout * m_pageGeneral; ///< The general configuration page. + KTouchPrefTrainingLayout * m_pageTraining; ///< The training configuration page. + KTouchPrefKeyboardLayout * m_pageKeyboard; ///< The keyboard configuration page. + KTouchPrefColorsLayout * m_pageColors; ///< The color scheme configuration page. + + QStringList m_lectureFiles; ///< A list of all default lecture files. + QStringList m_lectureTitles; ///< A list of the titles of all default lecture files. + + QStringList m_examinationFiles; ///< A list of all default examination files. + QStringList m_examinationTitles; ///< A list of the titles of all default examination files. + + QStringList m_keyboardFiles; ///< A list of all default keyboard layout files. + QStringList m_keyboardTitles; ///< A list of the titles of all default keyboard layout files. + + KTouchStatisticsData m_stats; ///< All user statistics are kept here. + + QChar m_lastDeadKey; ///< Temporary storage of last dead key. +}; + +/// A global pointer to the main widget (actually only used to retrieve some data). +extern KTouch * KTouchPtr; + +#endif // KTOUCH_H diff --git a/ktouch/src/ktouch.kcfg b/ktouch/src/ktouch.kcfg new file mode 100644 index 00000000..bf465639 --- /dev/null +++ b/ktouch/src/ktouch.kcfg @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="ktouchrc"/> + <group name="Colors" > + <entry type="Int" name="CurrentColorScheme" > + <label >The number of the current color scheme.</label> + <default >1</default> + </entry> + <entry type="Bool" name="CommonTypingLineColors" > + <label >Use the same typing line colors independent of color scheme.</label> + <default code="true" >false</default> + </entry> + <entry type="Color" name="TeacherBackgroundColor" > + <label >The background color for the teacher's line.</label> + <default >#bebeff</default> + </entry> + <entry type="Color" name="TeacherTextColor" > + <label >The text color for the teacher's line</label> + <default >#000032</default> + </entry> + <entry type="Color" name="StudentBackgroundColor" > + <label >The background color for the student's line.</label> + <default >#aoffao</default> + </entry> + <entry type="Color" name="StudentTextColor" > + <label >The text color for the student's line</label> + <default >#003200</default> + </entry> + <entry type="Bool" name="ColorOnError" > + <label >Whether to use a different background for wrong text or not.</label> + <default code="true" >true</default> + </entry> + <entry type="Color" name="ErrorBackgroundColor" > + <label >The background color for wrong text (students line).</label> + <default >#ac0000</default> + </entry> + <entry type="Color" name="ErrorTextColor" > + <label >The text color for wrong text (students line).</label> + <default >#ffffff</default> + </entry> + </group> + <group name="General" > + <entry type="Bool" name="BeepOnError" > + <label >Emit a beep on each typing error</label> + <default code="true" >true</default> + </entry> + <entry type="Bool" name="SoundOnLevel" > + <label >Play a sound on automatic level change</label> + <default code="true" >true</default> + </entry> + <entry type="Int" name="SlidingSpeed" > + <label >The sliding speed</label> + <default code="true" >5</default> + </entry> + <entry type="Int" name="MaxSlidingWidgetHeight" > + <label >The maximum height of the sliding widget</label> + <default code="true" >200</default> + </entry> + <entry type="Bool" name="OverrideLectureFont" > + <label >Override the default/predefined lecture fonts.</label> + <default code="true" >false</default> + </entry> + <entry type="Font" name="Font" > + <label >The font for the student and teacher lines</label> + <default code="true">QFont("Monospace")</default> + </entry> + <entry type="Path" name="CurrentLectureFile" > + <label >The currently loaded lecture file</label> + <default code="true">"default"</default> + </entry> + <entry type="Bool" name="Right2LeftTyping" > + <label >Whether we use right-to-left typing.</label> + <default code="true">false</default> + </entry> + </group> + <group name="Keyboard" > + <entry type="Bool" name="ShowAnimation" > + <label >Whether to use colors on the keys or not.</label> + <default code="true" >true</default> + </entry> + <entry type="Bool" name="HideKeyboard" > + <label >Whether to hide the keyboard display.</label> + <default code="true" >false</default> + </entry> + <entry type="Bool" name="OverrideKeyboardFont" > + <label >Whether to override the default/predefined keyboard fonts.</label> + <default code="true" >false</default> + </entry> + <entry type="Font" name="KeyboardFont" > + <label >The font for the keys on the keyboard</label> + <default >KGlobalSettings::generalFont()</default> + </entry> + <entry type="Path" name="CurrentKeyboardFile" > + <label >The currently loaded keyboard file</label> + <default >"number.keyboard"</default> + </entry> + </group> + <group name="Training" > + <entry type="Bool" name="AutoLevelChange" > + <label >Allow automatic level adjustments</label> + <default code="true" >true</default> + </entry> + <entry type="Bool" name="DisableManualLevelChange" > + <label >Disable manual level adjustments when auto-level change is enabled</label> + <default code="true" >false</default> + </entry> + <entry type="Bool" name="RememberLevel" > + <label >Remember the current level for the next KTouch start</label> + <default code="true" >true</default> + </entry> + <entry type="Int" name="CurrentTrainingLevel" > + <label >The current training level</label> + <default >0</default> + </entry> + <entry type="Int" name="UpSpeedLimit" > + <label >Number of chars per minute to increase a level</label> + <default >120</default> + </entry> + <entry type="Int" name="UpCorrectLimit" > + <label >Percentage of correctness to increase a level</label> + <default >85</default> + </entry> + <entry type="Int" name="DownSpeedLimit" > + <label >Number of chars per minute to decrease a level</label> + <default >60</default> + </entry> + <entry type="Int" name="DownCorrectLimit" > + <label >Percentage of correctness to decrease a level</label> + <default >60</default> + </entry> + </group> +</kcfg> diff --git a/ktouch/src/ktouchchartwidget.cpp b/ktouch/src/ktouchchartwidget.cpp new file mode 100644 index 00000000..aba87f28 --- /dev/null +++ b/ktouch/src/ktouchchartwidget.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * ktouchchartwidget.cpp * + * --------------------- * + * Copyright (C) 2005 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 "ktouchchartwidget.h" +#include "ktouchchartwidget.moc" + +#include <qpainter.h> + +#include <klocale.h> + +#include <numeric> // for mathematical functions +#include <cmath> + +KTouchChartWidget::KTouchChartWidget(QWidget* parent, const char *name) + : KPlotWidget(0.0, 1.0, 0.0, 1.0, parent, name) +{ + setShowGrid( false ); // no grid please + setBGColor( QColor( "white" ) ); + setFGColor( QColor( "black" ) ); + setLimits( 0.0, 120.0, 0.0, 240.0 ); + setXAxisLabel( i18n( "Progress" ) ); + setYAxisLabel( i18n( "Words per second" ) ); +} + +KTouchChartWidget::~KTouchChartWidget() +{ +} diff --git a/ktouch/src/ktouchchartwidget.h b/ktouch/src/ktouchchartwidget.h new file mode 100644 index 00000000..223705b0 --- /dev/null +++ b/ktouch/src/ktouchchartwidget.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * ktouchchartwidget.h * + * ------------------- * + * 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. * + ***************************************************************************/ + +#ifndef KTOUCHCHARTWIDGET +#define KTOUCHCHARTWIDGET + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <libkdeedu/kdeeduplot/kplotwidget.h> + +class KTouchTrainer; + +/// This is the chart widget and contains all chart drawing code. +class KTouchChartWidget : public KPlotWidget { + Q_OBJECT + public: + /// Constructor. + KTouchChartWidget(QWidget* parent, const char *name=0); + /// Destructor. + ~KTouchChartWidget(); +}; + +#endif // KTOUCHCHARTWIDGET diff --git a/ktouch/src/ktouchcoloreditor.cpp b/ktouch/src/ktouchcoloreditor.cpp new file mode 100644 index 00000000..1341366d --- /dev/null +++ b/ktouch/src/ktouchcoloreditor.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** + * ktouchcoloreditor.cpp * + * --------------------- * + * Copyright (C) 20064 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 "ktouchcoloreditor.h" +#include "ktouchcoloreditor.moc" + +#include <qlistbox.h> +#include <qgroupbox.h> + +#include <kcolorbutton.h> +#include <kdebug.h> +#include <klineedit.h> +#include <kpushbutton.h> +#include <klocale.h> +#include <kmessagebox.h> + +KTouchColorEditor::KTouchColorEditor(QWidget* parent, const char* name, bool modal, WFlags fl) +: KTouchColorEditorDlg(parent,name, modal,fl) +{ +} +// ---------------------------------------------------------------------------- + +KTouchColorEditor::~KTouchColorEditor() +{ +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::startEditor(QValueList<KTouchColorScheme>& schemes, int active, int & selected) { + m_schemes = schemes; + m_currentItem = QMAX(0, active); + updateListBox(); + + if (active >= 0) m_currentItem = active; + else m_currentItem = -1; + + exec(); + + if (m_saveChanges) { + selected = m_currentItem; + schemes = m_schemes; + } + else { + selected = active; + } +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::colorSchemeChanged(QListBoxItem *) { +// kdDebug() << "[KTouchColorEditor::colorSchemeChanged]" << endl; + int num = schemeListBox->currentItem(); + if (num < 0 || num >= static_cast<int>(m_schemes.count())) { + updateControls(NULL); + return; + } + QValueList<KTouchColorScheme>::const_iterator it = m_schemes.begin(); + while (--num >= 0) ++it; + updateControls(&(*it)); +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::updateClicked() { + // first check that we have a valid color scheme selected + int num = schemeListBox->currentItem(); + int old_num = num; + if (num < 0 || num >= static_cast<int>(m_schemes.count())) { + updateListBox(); + return; + } + QValueList<KTouchColorScheme>::iterator it = m_schemes.begin(); + while (--num >= 0) ++it; + (*it).m_name = nameEdit->text(); + if ((*it).m_name.isEmpty()) + (*it).m_name = i18n("New color scheme"); + // slide line colors + (*it).m_teacherTextColor = teacherTextBtn->color(); + (*it).m_teacherBackground = teacherBackBtn->color(); + (*it).m_studentTextColor = studentTextBtn->color(); + (*it).m_studentBackground = studentBackBtn->color(); + (*it).m_errorTextColor = studentErrorTextBtn->color(); + (*it).m_errorBackground = studentErrorBackBtn->color(); + // keyboard colors + (*it).m_frame = keyFrameBtn->color(); + (*it).m_text = keyTextBtn->color(); + (*it).m_textH = keyTextHighBtn->color(); + (*it).m_backgroundH = keyBackHighBtn->color(); + (*it).m_cText = cTextBtn->color(); + (*it).m_cBackground = cBackgroundBtn->color(); + (*it).m_cTextH = cTextHighBtn->color(); + (*it).m_cBackgroundH = cBackHighBtn->color(); + + (*it).m_background[0] = back1Btn->color(); + (*it).m_background[1] = back2Btn->color(); + (*it).m_background[2] = back3Btn->color(); + (*it).m_background[3] = back4Btn->color(); + (*it).m_background[4] = back5Btn->color(); + (*it).m_background[5] = back6Btn->color(); + (*it).m_background[6] = back7Btn->color(); + (*it).m_background[7] = back8Btn->color(); + + updateListBox(); + schemeListBox->setCurrentItem( QMIN(old_num, static_cast<int>(m_schemes.count())-1) ); + colorSchemeChanged(NULL); +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::removeBtnClicked() { + int num = schemeListBox->currentItem(); + int old_num = num; + if (num >=0 && num < static_cast<int>(m_schemes.count())) { + QValueList<KTouchColorScheme>::iterator it = m_schemes.begin(); + while (--num >= 0) ++it; + m_schemes.erase(it); + } + updateListBox(); + schemeListBox->setCurrentItem( QMIN(old_num, static_cast<int>(m_schemes.count())-1) ); + colorSchemeChanged(NULL); +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::addBtnClicked() { + KTouchColorScheme s; + s.m_name = i18n("New color scheme"); + m_schemes.append(s); + updateListBox(); + schemeListBox->setCurrentItem( m_schemes.count() - 1 ); + colorSchemeChanged(NULL); +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::closeEvent( QCloseEvent* ce ) { + int result = KMessageBox::questionYesNoCancel(this, + i18n("Save modified color schemes?")); + switch (result) { + case KMessageBox::Cancel : m_saveChanges = false; ce->ignore(); return; + case KMessageBox::Yes : + m_currentItem = schemeListBox->currentItem(); + updateClicked(); + m_saveChanges = true; break; + default : m_saveChanges = false; break; + } + ce->accept(); +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::updateListBox() { +// kdDebug() << "[KTouchColorEditor::updateListBox]" << endl; + schemeListBox->clear(); + for (QValueList<KTouchColorScheme>::const_iterator it = m_schemes.constBegin(); + it != m_schemes.constEnd(); ++it) + { + schemeListBox->insertItem( (*it).m_name ); + } + if (m_schemes.isEmpty()) { + editGroupBox->setEnabled(false); + removeBtn->setEnabled(false); + } + else { + editGroupBox->setEnabled(true); + removeBtn->setEnabled(true); + // select the first one by default + } +} +// ---------------------------------------------------------------------------- + +void KTouchColorEditor::updateControls(const KTouchColorScheme * cs) { +// kdDebug() << "[KTouchColorEditor::updateControls]" << endl; + if (cs==NULL) { + // TODO : also set all colors back to defaults + editGroupBox->setEnabled(false); + return; + } + editGroupBox->setEnabled(true); + nameEdit->setText(cs->m_name); + // slide line colors + teacherTextBtn->setColor(cs->m_teacherTextColor); + teacherBackBtn->setColor(cs->m_teacherBackground); + studentTextBtn->setColor(cs->m_studentTextColor); + studentBackBtn->setColor(cs->m_studentBackground); + studentErrorTextBtn->setColor(cs->m_errorTextColor); + studentErrorBackBtn->setColor(cs->m_errorBackground); + // keyboard colors + keyFrameBtn->setColor(cs->m_frame); + keyTextBtn->setColor(cs->m_text); + keyTextHighBtn->setColor(cs->m_textH); + keyBackHighBtn->setColor(cs->m_backgroundH); + cTextBtn->setColor(cs->m_cText); + cBackgroundBtn->setColor(cs->m_cBackground); + cTextHighBtn->setColor(cs->m_cTextH); + cBackHighBtn->setColor(cs->m_cBackgroundH); + + back1Btn->setColor(cs->m_background[0]); + back2Btn->setColor(cs->m_background[1]); + back3Btn->setColor(cs->m_background[2]); + back4Btn->setColor(cs->m_background[3]); + back5Btn->setColor(cs->m_background[4]); + back6Btn->setColor(cs->m_background[5]); + back7Btn->setColor(cs->m_background[6]); + back8Btn->setColor(cs->m_background[7]); +} +// ---------------------------------------------------------------------------- + + diff --git a/ktouch/src/ktouchcoloreditor.h b/ktouch/src/ktouchcoloreditor.h new file mode 100644 index 00000000..3d5a0e11 --- /dev/null +++ b/ktouch/src/ktouchcoloreditor.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * ktouchcoloreditor.h * + * ------------------- * + * Copyright (C) 20064 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. * + ***************************************************************************/ + +#ifndef KTOUCHCOLOREDITOR_H +#define KTOUCHCOLOREDITOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ktouchcoloreditor_dlg.h" + +#include <qvaluelist.h> + +#include "ktouchcolorscheme.h" + +/// Implementation of the KTouch Color Scheme Editor. +class KTouchColorEditor : public KTouchColorEditorDlg +{ + Q_OBJECT + +public: + /// Constructor + KTouchColorEditor(QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 ); + /// Destructor + ~KTouchColorEditor(); + + /// Use this function to run the dialog. + /// @param schemes List with user defined color schemes. + /// @param active Index with active color scheme (negative if predefined scheme is active). + /// @param selected Here the index of the currently selected scheme is stored. + void startEditor(QValueList<KTouchColorScheme>& schemes, int active, int & selected); + +public slots: + virtual void colorSchemeChanged(QListBoxItem * item); + virtual void updateClicked(); + virtual void removeBtnClicked(); + virtual void addBtnClicked(); + +protected: + virtual void closeEvent( QCloseEvent* ce ); + +private: + /// Updates the list box with values from m_schemes. + void updateListBox(); + /// Updates the button controls with data from 'cs', unless NULL is passed, in which case + /// the controls are disabled (no-color-scheme-selected state). + void updateControls(const KTouchColorScheme * cs); + + int m_currentItem; + QValueList<KTouchColorScheme> m_schemes; + bool m_saveChanges; +}; + +#endif // KTOUCHCOLOREDITOR_H + diff --git a/ktouch/src/ktouchcoloreditor_dlg.ui b/ktouch/src/ktouchcoloreditor_dlg.ui new file mode 100644 index 00000000..d6c4a0a7 --- /dev/null +++ b/ktouch/src/ktouchcoloreditor_dlg.ui @@ -0,0 +1,824 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchColorEditorDlg</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KTouchColorEditorDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>560</width> + <height>597</height> + </rect> + </property> + <property name="caption"> + <string>Color Scheme Editor</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="title"> + <string>User defined color schemes</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListBox" row="0" column="0" rowspan="3" colspan="1"> + <property name="name"> + <cstring>schemeListBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KPushButton" row="0" column="1"> + <property name="name"> + <cstring>addBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + <widget class="KPushButton" row="1" column="1"> + <property name="name"> + <cstring>removeBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>editGroupBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Edit color scheme:</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout18</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>nameL</cstring> + </property> + <property name="text"> + <string>Name of color scheme:</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>nameEdit</cstring> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>updateBtn</cstring> + </property> + <property name="text"> + <string>&Store data</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Slide line colors</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>teacherBackBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>Teacher background</string> + </property> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>teacherTextBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Teacher text</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3_5</cstring> + </property> + <property name="text"> + <string>Student text on error</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_4</cstring> + </property> + <property name="text"> + <string>Student background</string> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>studentBackBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="3" column="1"> + <property name="name"> + <cstring>studentErrorBackBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3_3</cstring> + </property> + <property name="text"> + <string>Student text</string> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>studentTextBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>studentErrorTextBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3_6</cstring> + </property> + <property name="text"> + <string>Student background on error</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>keyboardColorsGroup</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Keyboard colors</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout24</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="0" column="1"> + <property name="name"> + <cstring>spacer11_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="0" column="3"> + <property name="name"> + <cstring>spacer9_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer19</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="0" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>layout22</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel3_9</cstring> + </property> + <property name="text"> + <string>Highlighted text color</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3_8</cstring> + </property> + <property name="text"> + <string>Text color</string> + </property> + </widget> + <widget class="KColorButton" row="4" column="1"> + <property name="name"> + <cstring>keyTextHighBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3_7</cstring> + </property> + <property name="text"> + <string>Key frame</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Normal key colors:</string> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>keyFrameBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="3" column="1"> + <property name="name"> + <cstring>keyTextBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="5" column="1"> + <property name="name"> + <cstring>keyBackHighBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel3_10</cstring> + </property> + <property name="text"> + <string>Highlighted background</string> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer16</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QLayoutWidget" row="0" column="2"> + <property name="name"> + <cstring>layout20</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_7_2</cstring> + </property> + <property name="text"> + <string>Text color</string> + </property> + </widget> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>cBackgroundBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3_9_2</cstring> + </property> + <property name="text"> + <string>Highlighted text color</string> + </property> + </widget> + <widget class="KColorButton" row="4" column="1"> + <property name="name"> + <cstring>cBackHighBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel3_10_2</cstring> + </property> + <property name="text"> + <string>Highlighted background</string> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>cTextBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="3" column="1"> + <property name="name"> + <cstring>cTextHighBtn</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Modifier/other keys:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3_8_2</cstring> + </property> + <property name="text"> + <string>Background</string> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_11</cstring> + </property> + <property name="text"> + <string>Background colors for normal keys:</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout28</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KColorButton"> + <property name="name"> + <cstring>back1Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back2Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back3Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back4Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back5Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back6Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back7Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>back8Btn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>addBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchColorEditorDlg</receiver> + <slot>addBtnClicked()</slot> + </connection> + <connection> + <sender>removeBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchColorEditorDlg</receiver> + <slot>removeBtnClicked()</slot> + </connection> + <connection> + <sender>schemeListBox</sender> + <signal>currentChanged(QListBoxItem*)</signal> + <receiver>KTouchColorEditorDlg</receiver> + <slot>colorSchemeChanged(QListBoxItem*)</slot> + </connection> + <connection> + <sender>updateBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchColorEditorDlg</receiver> + <slot>updateClicked()</slot> + </connection> +</connections> +<slots> + <slot>closeQuery()</slot> + <slot>addBtnClicked()</slot> + <slot>removeBtnClicked()</slot> + <slot>updateClicked()</slot> + <slot>colorSchemeChanged(QListBoxItem * item)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchcolorscheme.cpp b/ktouch/src/ktouchcolorscheme.cpp new file mode 100644 index 00000000..0092fb07 --- /dev/null +++ b/ktouch/src/ktouchcolorscheme.cpp @@ -0,0 +1,309 @@ +/*************************************************************************** + * ktouchcolorscheme.cpp * + * --------------------- * + * Copyright (C) 2003-2006 by Andreas Nicolai * + * * + * 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 "ktouchcolorscheme.h" + +#include <qfile.h> +#include <qtextstream.h> +#include <qdom.h> + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kio/netaccess.h> + +#define WRITE_COLOR(z,x,y) e = doc.createElement(x); \ + n = doc.createTextNode( (y).name() ); \ + e.appendChild(n); \ + z.appendChild(e); + +#define READ_COLOR(x,y) c = n.namedItem(x); \ + if (!c.isNull()) y = QColor(c.firstChild().nodeValue()); + +QValueVector<KTouchColorScheme> KTouchColorScheme::m_colorSchemes; + +void KTouchColorScheme::clear() { + // the default scheme is the classic one + m_teacherTextColor = QColor("#000032"); + m_teacherBackground = QColor("#BEBEFF"); + m_studentTextColor = QColor("#003200"); + m_studentBackground = QColor("#9FFF9F"); + m_errorTextColor = Qt::white; + m_errorBackground = QColor("#AC0000"); + + m_frame = Qt::black; + m_background[0] = QColor(255,238, 7); m_background[4] = QColor(247,138,247); + m_background[1] = QColor( 14,164,239); m_background[5] = QColor(158,255,155); + m_background[2] = QColor(158,255,155); m_background[6] = QColor( 14,164,239); + m_background[3] = QColor(252,138,138); m_background[7] = QColor(255,238, 7); + m_text = Qt::black; + m_backgroundH = Qt::darkBlue; + m_textH = Qt::white; + m_cBackground = Qt::gray; + m_cText = Qt::black; + m_cBackgroundH = Qt::white; + m_cTextH = Qt::black; + + m_default = false; +} +// ---------------------------------------------------------------------------- + +bool KTouchColorScheme::read(const QDomNode& node) { +// kdDebug() << "[KTouchColorScheme::read] " << endl; + + QDomNode name = node.namedItem("Name"); + if (name.isNull()) m_name = i18n("untitled color scheme"); + else m_name = name.firstChild().nodeValue(); + +// kdDebug() << " Reading scheme = " << m_name << endl; + + QDomNode n = node.namedItem("LineColors"); + if (!n.isNull()) { + QDomNode c; + READ_COLOR("TeacherTextColor", m_teacherTextColor); + READ_COLOR("TeacherBackground", m_teacherBackground); + READ_COLOR("StudentTextColor", m_studentTextColor); + READ_COLOR("StudentBackground", m_studentBackground); + READ_COLOR("ErrorTextColor", m_errorTextColor); + READ_COLOR("ErrorBackground", m_errorBackground); + } + + n = node.namedItem("KeyboardColors"); + if (!n.isNull()) { + QDomNode c; + READ_COLOR("KeyFrame", m_frame); + for (unsigned int i=0; i<8; ++i) { + READ_COLOR(QString("KeyBack_%1").arg(i), m_background[i]); + } + READ_COLOR("KeyText", m_text); + READ_COLOR("KeyBackHigh", m_backgroundH); + READ_COLOR("KeyTextHigh", m_textH); + READ_COLOR("KeyBackControl", m_cBackground); + READ_COLOR("KeyTextControl", m_cText); + READ_COLOR("KeyBackControlHigh", m_cBackgroundH); + READ_COLOR("KeyTextControlHigh", m_cTextH); + } + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchColorScheme::write(QDomDocument& doc, QDomElement& root) const { + //kdDebug() << "[KTouchColorScheme::write] " << endl; + QDomElement element = doc.createElement("KTouchColorScheme"); + // append name of color scheme + QDomElement name = doc.createElement("Name"); + QDomText name_text; + if (m_name.isEmpty()) name_text = doc.createTextNode( i18n("untitled color scheme") ); + else name_text = doc.createTextNode(m_name); + name.appendChild(name_text); + element.appendChild(name); + // store slide line colors + QDomElement line_colors = doc.createElement("LineColors"); + QDomElement e; + QDomText n; + + WRITE_COLOR(line_colors, "TeacherTextColor", m_teacherTextColor); + WRITE_COLOR(line_colors, "TeacherBackground", m_teacherBackground); + WRITE_COLOR(line_colors, "StudentTextColor", m_studentTextColor); + WRITE_COLOR(line_colors, "StudentBackground", m_studentBackground); + WRITE_COLOR(line_colors, "ErrorTextColor", m_errorTextColor); + WRITE_COLOR(line_colors, "ErrorBackground", m_errorBackground); + + element.appendChild(line_colors); + // done with slide line colors + + // write key drawing colors + QDomElement key_colors = doc.createElement("KeyboardColors"); + + WRITE_COLOR(key_colors, "KeyFrame", m_frame); + for (unsigned int i=0; i<8; ++i) { + WRITE_COLOR(key_colors, QString("KeyBack_%1").arg(i), m_background[i]); + } + WRITE_COLOR(key_colors, "KeyText", m_text); + WRITE_COLOR(key_colors, "KeyBackHigh", m_backgroundH); + WRITE_COLOR(key_colors, "KeyTextHigh", m_textH); + WRITE_COLOR(key_colors, "KeyBackControl", m_cBackground); + WRITE_COLOR(key_colors, "KeyTextControl", m_cText); + WRITE_COLOR(key_colors, "KeyBackControlHigh", m_cBackgroundH); + WRITE_COLOR(key_colors, "KeyTextControlHigh", m_cTextH); + + element.appendChild(key_colors); + + + root.appendChild(element); +} +// ---------------------------------------------------------------------------- + +bool KTouchColorScheme::readList(QWidget * window, const KURL& url) { + if (url.isEmpty()) return false; + //kdDebug() << "[KTouchColorScheme::readList] " << endl; + //kdDebug() << " url = '" << url.url() << "'" << endl; + QString tmp_file; + bool result = KIO::NetAccess::download(url, tmp_file, window); + if (result) { + // Ok, that was successful, try to parse the XML doc now + QFile infile(tmp_file); + if ( !infile.open( IO_ReadOnly ) ) { + KIO::NetAccess::removeTempFile(tmp_file); + return false; // Bugger it... couldn't open it... + } + QDomDocument doc; + doc.setContent( &infile ); + // read the document + QDomNodeList schemes = doc.elementsByTagName("KTouchColorScheme"); + int num = schemes.count(); + if (num > 0) { + //kdDebug() << " reading " << schemes.count() << " color maps..." << endl; + for (int i=0; i<num; ++i) { + KTouchColorScheme scheme; + if (scheme.read(schemes.item(i))) + m_colorSchemes.append(scheme); + } + } + + } + KIO::NetAccess::removeTempFile(tmp_file); + + return result; +} +// ---------------------------------------------------------------------------- + +bool KTouchColorScheme::writeList(QWidget * window, const KURL& url) { + // create the XML document and root node + QDomDocument doc; + QDomElement root = doc.createElement( "KTouchColorSchemeCollection" ); + doc.appendChild(root); + // write all the non-default color schemes + for (QValueVector<KTouchColorScheme>::const_iterator it = m_colorSchemes.constBegin(); + it != m_colorSchemes.constEnd(); ++it) + { + if (!it->m_default) it->write(doc, root); + } + + // and save it + QString tmp_file; + KTempFile *temp=0; + if (url.isLocalFile()) + tmp_file=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + temp->setAutoDelete(true); // so we don't have to delete the file ourselves + tmp_file=temp->name(); + } + + QFile outfile(tmp_file); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + kdDebug() << "Error creating tmp file '"+tmp_file+"' for writing color map!" << endl; + return false; + } + + QTextStream out( &outfile ); + out << doc.toString(); + outfile.close(); + // if we have a temporary file, we still need to upload it + if (temp) { + KIO::NetAccess::upload(tmp_file, url, window); + delete temp; + } + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchColorScheme::createDefaults() { + m_colorSchemes.clear(); + KTouchColorScheme color; + color.m_default = true; + + color.m_name = i18n("Black & White"); + color.m_teacherTextColor = Qt::black; + color.m_teacherBackground = Qt::white; + color.m_studentTextColor = Qt::black; + color.m_studentBackground = Qt::white; + color.m_errorTextColor = Qt::white; + color.m_errorBackground = Qt::black; + color.m_frame = Qt::black; + for (int i=0; i<8; ++i) + color.m_background[i] = Qt::white; + color.m_text = Qt::black; + color.m_backgroundH = Qt::black; + color.m_textH = Qt::white; + color.m_cBackground = Qt::gray; + color.m_cText = Qt::black; + color.m_cBackgroundH = Qt::white; + color.m_cTextH = Qt::black; + m_colorSchemes.push_back(color); + + color.m_name = i18n("Classic"); + color.m_teacherTextColor = QColor("#000032"); + color.m_teacherBackground = QColor("#BEBEFF"); + color.m_studentTextColor = QColor("#003200"); + color.m_studentBackground = QColor("#9FFF9F"); + color.m_errorTextColor = Qt::white; + color.m_errorBackground = QColor("#AC0000"); + color.m_frame = Qt::black; + color.m_background[0] = QColor(255,238, 7); color.m_background[4] = QColor(247,138,247); + color.m_background[1] = QColor( 14,164,239); color.m_background[5] = QColor(158,255,155); + color.m_background[2] = QColor(158,255,155); color.m_background[6] = QColor( 14,164,239); + color.m_background[3] = QColor(252,138,138); color.m_background[7] = QColor(255,238, 7); + color.m_text = Qt::black; + color.m_backgroundH = Qt::darkBlue; + color.m_textH = Qt::white; + color.m_cBackground = Qt::gray; + color.m_cText = Qt::black; + color.m_cBackgroundH = Qt::white; + color.m_cTextH = Qt::black; + m_colorSchemes.push_back(color); + + color.m_name = i18n("Deep Blue"); + color.m_teacherTextColor = Qt::white; + color.m_teacherBackground = QColor( 0, 39, 80); + color.m_studentTextColor = Qt::white; + color.m_studentBackground = QColor( 39, 59,127); + color.m_errorTextColor = QColor("#B5CFFF"); + color.m_errorBackground = QColor("#640000"); + color.m_frame = QColor(220,220,220); + color.m_background[0] = QColor( 0, 39, 80); color.m_background[4] = QColor( 24, 19, 72); + color.m_background[1] = QColor( 39, 59,127); color.m_background[5] = QColor( 8, 44,124); + color.m_background[2] = QColor( 4, 39, 53); color.m_background[6] = QColor( 10, 82,158); + color.m_background[3] = QColor( 40, 32,121); color.m_background[7] = QColor( 43, 60,124); + color.m_text = Qt::white; + color.m_backgroundH = QColor(125,180,255); + color.m_textH = Qt::darkBlue; + color.m_cBackground = Qt::black; + color.m_cText = Qt::white; + color.m_cBackgroundH = QColor(111,121,73); + color.m_cTextH = Qt::white; + m_colorSchemes.push_back(color); + + color.m_name = i18n("Stripy"); + color.m_teacherTextColor = Qt::white; + color.m_teacherBackground = QColor( 39, 70, 227); + color.m_studentTextColor = Qt::white; + color.m_studentBackground = QColor( 39, 70, 127); + color.m_errorTextColor = Qt::white; + color.m_errorBackground = Qt::darkGray; + color.m_frame = Qt::black; + for (int i=0; i<8; i=i+2) + color.m_background[i] = QColor( 39, 70, 127); + for (int i=1; i<8; i=i+2) + color.m_background[i] = Qt::darkGray; + color.m_text = Qt::black; + color.m_backgroundH = QColor( 39, 70, 227); + color.m_textH = Qt::white; + color.m_cBackground = Qt::gray; + color.m_cText = Qt::black; + color.m_cBackgroundH = QColor( 39, 70, 227); + color.m_cTextH = Qt::black; + m_colorSchemes.push_back(color); +} +// ---------------------------------------------------------------------------- + diff --git a/ktouch/src/ktouchcolorscheme.h b/ktouch/src/ktouchcolorscheme.h new file mode 100644 index 00000000..47e28813 --- /dev/null +++ b/ktouch/src/ktouchcolorscheme.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * ktouchcolorscheme.h * + * ------------------- * + * Copyright (C) 2003 by Andreas Nicolai * + * * + * 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. * + ***************************************************************************/ + +#ifndef KTOUCHCOLORSCHEME_H +#define KTOUCHCOLORSCHEME_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qcolor.h> +#include <qvaluevector.h> + +#include <kurl.h> + +class QDomDocument; +class QDomElement; +class QDomNode; + +/// Contains all colours of the colour scheme. +/// +/// This is the colour scheme used for the slide line widget and the keyboard widget. +class KTouchColorScheme { + public: + /// Default constructor. + KTouchColorScheme() { clear(); } + /// Creates the default colour scheme. + void clear(); + + /// Loads a color map (in XML format) from XML node (returns true if successful) + bool read(const QDomNode& node); + /// Saves a color map to XML document (returns true if successful). + void write(QDomDocument& doc, QDomElement& root) const; + + + QString m_name; ///< The name of the colour scheme. + + QColor m_teacherTextColor; ///< The text colour for the teachers line. + QColor m_teacherBackground; ///< The background colour for the teachers line. + QColor m_studentTextColor; ///< The text colour for the students line. + QColor m_studentBackground; ///< The background colour for the students line. + QColor m_errorTextColor; ///< The text colour for wrong text (students line). + QColor m_errorBackground; ///< The background colour for wrong text (students line). + + QColor m_frame; ///< The colour for the key frames. + QColor m_background[8]; ///< The background colour of the finger keys and the associated normal keys. + QColor m_text; ///< The colour for the text on the keys. + QColor m_backgroundH; ///< The background colour for highlighted (next) normal keys. + QColor m_textH; ///< The text/pen colour for highlighted (next) normal keys. + QColor m_cBackground; ///< The background colour for the control keys. + QColor m_cText; ///< The text/pen colour for the control keys. + QColor m_cBackgroundH; ///< The background colour for activated control keys. + QColor m_cTextH; ///< The text/pen colour for activated control keys. + + bool m_default; ///< If true, this color map is marked as default and will not + /// be saved in the XML file. + + // *** static member functions *** + + /// Loads several color maps (in XML format) from file (returns true if successful) + /// and _adds_ them to the current color maps. This function shall be called + /// directly after createDefaultColorSchemes(). + static bool readList(QWidget * window, const KURL& url); + /// Saves several color maps to file (returns true if successful). + /// Only non-default color maps are written to file. + static bool writeList(QWidget * window, const KURL& url); + /// Creates some default color schemes (erases m_colorSchemes before). + static void createDefaults(); + /// Contains all color schemes available in the program. + static QValueVector<KTouchColorScheme> m_colorSchemes; +}; + +#endif // KTOUCHCOLORSCHEME_H + diff --git a/ktouch/src/ktouchdefaults.cpp b/ktouch/src/ktouchdefaults.cpp new file mode 100644 index 00000000..ac95a98c --- /dev/null +++ b/ktouch/src/ktouchdefaults.cpp @@ -0,0 +1,23 @@ +/*************************************************************************** + * ktouchdefaults.cpp * + * ------------------ * + * Copyright (C) 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 "ktouchdefaults.h" + +const unsigned int BEGINNER_SPEED = 60; // in characters per minute +const unsigned int ADVANCED_SPEED = 120; // in characters per minute +const unsigned int PROFESSIONAL_SPEED = 220; // in characters per minute + +// the margin between keyboard and widget frame +const int KEYBOARD_MARGIN = 10; + +// milli seconds between updates of the speed LCD +const int LCD_UPDATE_INTERVAL = 500; diff --git a/ktouch/src/ktouchdefaults.h b/ktouch/src/ktouchdefaults.h new file mode 100644 index 00000000..d66a1663 --- /dev/null +++ b/ktouch/src/ktouchdefaults.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * ktouchdefaults.h * + * ------------------- * + * Copyright (C) 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. * + ***************************************************************************/ + +#ifndef KTOUCHDEFAULTS_H +#define KTOUCHDEFAULTS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +extern const unsigned int BEGINNER_SPEED; ///< Speed for rating in characters per minute. +extern const unsigned int ADVANCED_SPEED; ///< Speed for rating in characters per minute. +extern const unsigned int PROFESSIONAL_SPEED; ///< Speed for rating in characters per minute. + +extern const int KEYBOARD_MARGIN; ///< The margin around the keybaord layout. + +extern const int LCD_UPDATE_INTERVAL; ///< Update interval for LCD display and statistics. + +#endif // KTOUCHDEFAULTS_H diff --git a/ktouch/src/ktouchkey.cpp b/ktouch/src/ktouchkey.cpp new file mode 100644 index 00000000..cb68b214 --- /dev/null +++ b/ktouch/src/ktouchkey.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * ktouchkey.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 "ktouchkey.h" +#include <kdebug.h> +#include <utility> // for std::min + +KTouchKey::KTouchKey(keytype_t type, int x, int y, int w, int h, QChar ch) + : m_type(type), m_x(x), m_y(y), m_w(w), m_h(h) +{ + m_chars[0].m_ch = ch; + m_chars[0].m_bold = true; + m_chars[0].m_pos = KTouchKeyChar::TOP_LEFT; +} +// ---------------------------------------------------------------------------- + +KTouchKey::KTouchKey(int x, int y, int w, int h, QString text) : + m_x(x), m_y(y), m_w(w), m_h(h) +{ + m_type = OTHER; + m_chars[0].m_ch = 0; + m_chars[0].m_bold = true; + m_chars[0].m_pos = KTouchKeyChar::TOP_LEFT; + m_chars[0].m_text = text; +} +// ---------------------------------------------------------------------------- + +void KTouchKey::resize(double scale) { + m_xS = static_cast<int>(scale*m_x); + m_yS = static_cast<int>(scale*m_y); + m_wS = static_cast<int>(scale*m_w); + m_hS = static_cast<int>(scale*m_h); +} +// ----------------------------------------------------------------------------- + + +// Reads the key data from the DomElement +bool KTouchKey::read(QDomNode node) { +/* + if (node.isNull()) + return false; // TODO : Error message + QString primaryCharText = node.firstChild().nodeValue(); + if (primaryCharText.length() >= 1) + m_primaryChar = primaryCharText[0]; + else + return false; // TODO : Error message + QDomNamedNodeMap nmap = node.attributes(); + // Get height, widht, x and y + node = nmap.namedItem("Height"); + if (node.isNull()) + return false; // TODO : Error message + m_h = node.nodeValue().toInt(); + node = nmap.namedItem("Width"); + if (node.isNull()) + return false; // TODO : Error message + m_w = node.nodeValue().toInt(); + node = nmap.namedItem("X"); + if (node.isNull()) + return false; // TODO : Error message + m_x = node.nodeValue().toInt(); + node = nmap.namedItem("Y"); + if (node.isNull()) + return false; // TODO : Error message + m_y = node.nodeValue().toInt(); + // read type of key + node = nmap.namedItem("Type"); + if (node.isNull()) + return false; // TODO : Error message + QString typetext = node.nodeValue(); + if (typetext=="NORMAL") m_type = NORMAL; + else if (typetext=="FINGER") m_type = FINGER; + else if (typetext=="ENTER") m_type = ENTER; + else if (typetext=="BACKSPACE") m_type = BACKSPACE; + else if (typetext=="SHIFT") m_type = SHIFT; + else if (typetext=="SPACE") m_type = SPACE; + else if (typetext=="OTHER") { + m_type = OTHER; + node = nmap.namedItem("OtherKeyText"); + if (!node.isNull()) + m_otherKeyText = node.nodeValue(); + } + else + return false; // TODO : Error message + // read optional secondary character + node = nmap.namedItem("SecondaryChar"); + QString charvalue; + if (!node.isNull()) + charvalue = node.nodeValue(); + if (charvalue.length() >= 1) + m_secondaryChar = charvalue[0]; + else + m_secondaryChar = QChar(0); + kdDebug() << "H:" << m_h << " W:" << m_w << " X:" << m_x << " Y:" << m_y + << " Type:" << m_type << " SecondaryChar:" << m_secondaryChar + << "' PrimaryChar:" << m_primaryChar << "'" << endl; +*/ + return true; +} +// ---------------------------------------------------------------------------- + +// Writes the key data into the DomElement +void KTouchKey::write(QDomDocument& doc, QDomElement& root) const { +/* + QDomElement element = doc.createElement("KeyDefinition"); + switch (m_type) { + case NORMAL : element.setAttribute("Type", "NORMAL"); break; + case FINGER : element.setAttribute("Type", "FINGER"); break; + case ENTER : element.setAttribute("Type", "ENTER"); break; + case BACKSPACE : element.setAttribute("Type", "BACKSPACE"); break; + case SHIFT : element.setAttribute("Type", "SHIFT"); break; + case SPACE : element.setAttribute("Type", "SPACE"); break; + case OTHER : + element.setAttribute("Type", "OTHER"); + element.setAttribute("OtherKeyText", m_otherKeyText); + break; + } + QDomText charnode = doc.createTextNode(QString(m_primaryChar)); + element.appendChild(charnode); +// element.setAttribute("PrimaryChar", QString(m_primaryChar)); + if (m_secondaryChar!=QChar(0)) + element.setAttribute("SecondaryChar", QString(m_secondaryChar)); + element.setAttribute("X", m_x); + element.setAttribute("Y", m_y); + element.setAttribute("Width", m_w); + element.setAttribute("Height", m_h); + root.appendChild(element); +*/ +} +// ---------------------------------------------------------------------------- + diff --git a/ktouch/src/ktouchkey.h b/ktouch/src/ktouchkey.h new file mode 100644 index 00000000..f27fd80f --- /dev/null +++ b/ktouch/src/ktouchkey.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * ktouchkey.h * + * ----------- * + * 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. * + ***************************************************************************/ + +#ifndef KTOUCHKEY_H +#define KTOUCHKEY_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qpainter.h> +#include <qdom.h> + +/// This class contains information about one character on a key. +class KTouchKeyChar { + public: + /// Position of the character on the key. + enum position_t { + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT + }; + + /// Constructor. + KTouchKeyChar() {} + /// Constructor. + KTouchKeyChar(QChar ch, position_t p, bool bold = false) : + m_ch(ch), m_pos(p), m_bold(bold) {} + + QChar m_ch; ///< The character to draw. + position_t m_pos; ///< The position of the character. + bool m_bold; ///< Whether this is a bold character. + + QString m_text; ///< The text to draw of m_ch == 0. +}; + +/// This class represents a key on the keyboard. +/// The primary character is the identification character for the key and will +/// be printed top left of the key (like normal character keys). If a secondary +/// character is given (as for the keys containing numbers), the primary key will +/// be printed bottom left and the secondary key will be printed top left. +/// If m_secondaryChar is 0, no secondary character is given. +class KTouchKey { + public: + enum keytype_t { + NORMAL, + FINGER, + ENTER, + BACKSPACE, + SHIFT, + SPACE, + OTHER + }; + + /// Default constructor + KTouchKey() : m_type(NORMAL), m_x(0), m_y(0), m_w(0), m_h(0) {} + /// Convenience constructor for a key with a single character (like before). + KTouchKey(keytype_t type, int x, int y, int w, int h, QChar ch); + /// Convenience constructor for a key with a text on it (type will be OTHER). + KTouchKey(int x, int y, int w, int h, QString text); + + /// Resizes the key (this function will be obsolete soon) + void resize(double scale); + + /// Reads the key data from the DomElement + bool read(QDomNode node); + /// Creates a new DomElement, writes the key data into it and appends it to the root object. + void write(QDomDocument& doc, QDomElement& root) const; + + unsigned int m_number; ///< The number of the key. + keytype_t m_type; ///< The type of the key. + KTouchKeyChar m_chars[4]; ///< The key character information. + int m_x; ///< The x-coordinate of the top-left corner of the key. + int m_y; ///< The y-coordinate of the top-left corner of the key. + int m_w; ///< The width. + int m_h; ///< The height. + + int m_xS; ///< The scaled x-coordinate of the top-left corner of the key. + int m_yS; ///< The scaled y-coordinate of the top-left corner of the key. + int m_wS; ///< The scaled width. + int m_hS; ///< The scaled height. + +}; +// --------------------------------------------------------------------------------------- + +#endif // KTOUCHKEYS_H diff --git a/ktouch/src/ktouchkeyboard.cpp b/ktouch/src/ktouchkeyboard.cpp new file mode 100644 index 00000000..a9650f9e --- /dev/null +++ b/ktouch/src/ktouchkeyboard.cpp @@ -0,0 +1,658 @@ +/*************************************************************************** + * ktouchkeyboard.cpp * + * ------------------ * + * Copyright (C) 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 "ktouchkeyboard.h" + +#include <qfile.h> + +#include <kdebug.h> +#include <ktempfile.h> +#include <kio/netaccess.h> +#include <klocale.h> + +#include "prefs.h" + +// -------------------------------------------------------------------------- + +// Clears the keyboard data +void KTouchKeyboard::clear() { + m_keys.clear(); + m_connectors.clear(); + m_title = QString::null; + m_comment = QString::null; + m_language = QString::null; + m_fontSuggestions = QString::null; +} +// ---------------------------------------------------------------------------- + +// Loads a keyboard layout (old format) from file (returns true if successful). +bool KTouchKeyboard::load(QWidget * window, const KURL& url) { + // Ok, first download the contents as usual using the KIO lib + // File is only downloaded if not local, otherwise it's just opened + QString target; + bool result = false; + //kdDebug() << "[KTouchKeyboard::load] " << url << endl; + if (KIO::NetAccess::download(url, target, window)) { + // Ok, that was successful, store the lectureURL and read the file + QFile infile(target); + if ( !infile.open( IO_ReadOnly ) ) + return false; // Bugger it... couldn't open it... + QTextStream in( &infile ); + result = read(in); + }; + KIO::NetAccess::removeTempFile(target); + return result; +} +// ---------------------------------------------------------------------------- + +// Loads a lecture (in XML format) from file (returns true if successful). +bool KTouchKeyboard::loadXML(QWidget * window, const KURL& url) { + // Ok, first download the contents as usual using the KIO lib + // File is only downloaded if not local, otherwise it's just opened + QString target; + bool result = false; + if (KIO::NetAccess::download(url, target, window)) { + // Ok, that was successful, store the lectureURL and read the file + QFile infile(target); + if ( !infile.open( IO_ReadOnly ) ) + return false; // Bugger it... couldn't open it... + QDomDocument doc; + doc.setContent( &infile ); + result = read(doc); + } + KIO::NetAccess::removeTempFile(target); + return result; +} +// ---------------------------------------------------------------------------- + +// Saves the lecture data to file (returns true if successful). +bool KTouchKeyboard::saveXML(QWidget * window, const KURL& url) const { + // create the XML document + QDomDocument doc; + write(doc); + + // and save it + QString tmpFile; + KTempFile *temp=0; + if (url.isLocalFile()) + tmpFile=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + temp->setAutoDelete(true); // so we don't have to delete the file ourselves + tmpFile=temp->name(); + } + + QFile outfile(tmpFile); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + // kdDebug() << "Error creating lecture file!" << endl; + return false; + }; + + QTextStream out( &outfile ); + out << doc.toString(); + outfile.close(); + // if we have a temporary file, we still need to upload it + if (temp) { + KIO::NetAccess::upload(tmpFile, url, window); + delete temp; + } + return true; +} +// ---------------------------------------------------------------------------- + +// Loads keyboard data from file, preserved for compatibility +bool KTouchKeyboard::read(QTextStream& in) { + in.setEncoding(QTextStream::UnicodeUTF8); + QString line; + clear(); // empty the keyboard + // now loop until end of file is reached + do { + // skip all empty lines or lines containing a comment (starting with '#') + do { line = in.readLine().stripWhiteSpace(); } + while (!line.isNull() && (line.isEmpty() || line[0]=='#')); + // Check if end of file encountered and if that is the case -> bail out at next while + if (line.isNull()) continue; + + // 'line' should now contain a key specification + QTextStream lineStream(line, IO_ReadOnly); + QString keyType; + int keyAscII; + QString keyText; + int x(0), y(0), w(0), h(0); + lineStream >> keyType >> keyAscII; + if (keyType=="FingerKey") { + lineStream >> keyText >> x >> y >> w >> h; + if (w==0 || h==0) + w=h=8; // default values for old keyboard files + KTouchKey key(KTouchKey::FINGER, keyText[0], 0, x+1, y+1, w, h); + m_keys.push_back(key); + KTouchKeyConnector keycon(keyText[0], keyText[0], 0, 0); + m_connectors.push_back(keycon); + kdDebug() << "[KTouchKeyboard::read] FingerKey '" << keyText[0] << "'" << endl; + } + else if (keyType=="ControlKey") { + lineStream >> keyText >> x >> y >> w >> h; + KTouchKey key(KTouchKey::OTHER, 0, 0, x+1, y+1, w-2, h-2); + key.m_otherKeyText = keyText; + m_keys.push_back(key); + kdDebug() << "[KTouchKeyboard::read] ControlKey '" << keyText << "'" << endl; + } + else if (keyType=="NormalKey") { + int fingerCharCode; + lineStream >> keyText >> x >> y >> fingerCharCode; + w=h=8; // default values for old keyboard files + KTouchKey key(KTouchKey::NORMAL, keyText[0], 0, x+1, y+1, w, h); + m_keys.push_back(key); + KTouchKeyConnector keycon(keyText[0], keyText[0], fingerCharCode, 0); + m_connectors.push_back(keycon); + kdDebug() << "[KTouchKeyboard::read] NormalKey '" << keyText[0] << "' f = '"<< fingerCharCode<< "'" << endl; + } else if (keyType=="HiddenKey") { + int targetChar, fingerChar, controlChar; + lineStream >> targetChar >> fingerChar >> controlChar; + KTouchKeyConnector keycon(targetChar, targetChar, fingerChar, 0); + m_connectors.push_back(keycon); + } + else { + //errorMsg = i18n("Missing key type in line '%1'.").arg(line); + return false; + } + // calculate the maximum extent of the keyboard on the fly... + } while (!in.atEnd() && !line.isNull()); + + return (!m_keys.isEmpty()); // empty file means error +} +// ---------------------------------------------------------------------------- + +// Loads keyboard data from file into an XML document +bool KTouchKeyboard::read(const QDomDocument& doc) { + // clean current data + kdDebug() << "Reading new keyboard layout" << endl; + m_keys.clear(); + m_connectors.clear(); + m_title = QString::null; + // retrieve the title + QDomNodeList entries = doc.elementsByTagName("Title"); + if (entries.count() >= 1) m_title = entries.item(0).firstChild().nodeValue(); + else m_title = i18n("untitled keyboard layout"); + kdDebug() << "Title: " << m_title << endl; + // retrieve the comment + entries = doc.elementsByTagName("Comment"); + if (entries.count() >= 1) + m_comment = entries.item(0).firstChild().nodeValue(); + // retrieve the font suggestion + entries = doc.elementsByTagName("FontSuggestions"); + if (entries.count() >= 1) + m_fontSuggestions = entries.item(0).firstChild().nodeValue(); + // retrieve the language id + entries = doc.elementsByTagName("Language"); + if (entries.count() >= 1) + m_language = entries.item(0).firstChild().nodeValue(); + // retrieve the key definitions + entries = doc.elementsByTagName("KeyDefinitions"); + QDomNode node = entries.item(0).firstChild(); + while (!node.isNull()) { + KTouchKey key; + if (key.read(node)) + m_keys.push_back(key); + // TODO : Error message if reading failed + node = node.nextSibling(); + } + // retrieve the key definitions + entries = doc.elementsByTagName("KeyConnections"); + node = entries.item(0).firstChild(); + while (!node.isNull()) { + KTouchKeyConnector keycon; + if (keycon.read(node)) + m_connectors.push_back(keycon); + // TODO : Error message if reading failed + node = node.nextSibling(); + } + if (!m_title.isEmpty()) + return true; // if we could read a title, let's assume all is ok + else { + // Hmm, no levels in the file. So we create our default mini level and report an error. + createDefault(); + return false; + }; + return false; +} +// ---------------------------------------------------------------------------- + +// Saves keyboard data in the XML document +void KTouchKeyboard::write(QDomDocument& doc) const { + QDomElement root = doc.createElement( "KTouchKeyboard" ); + doc.appendChild(root); + // Store title and ensure that the file contains a title! + QDomElement title = doc.createElement("Title"); + QDomText titleText; + if (m_title.isEmpty()) titleText = doc.createTextNode( i18n("untitled keyboard layout") ); + else titleText = doc.createTextNode(m_title); + title.appendChild(titleText); + root.appendChild(title); + // Store comment if given + if (!m_comment.isEmpty()) { + QDomElement e = doc.createElement("Comment"); + QDomText t = doc.createTextNode(m_comment); + e.appendChild(t); + root.appendChild(e); + } + // Store font suggestion if given + if (!m_fontSuggestions.isEmpty()) { + QDomElement e = doc.createElement("FontSuggestions"); + QDomText t = doc.createTextNode(m_fontSuggestions); + e.appendChild(t); + root.appendChild(e); + } + // Store language idif given + if (!m_language.isEmpty()) { + QDomElement e = doc.createElement("Language"); + QDomText t = doc.createTextNode(m_language); + e.appendChild(t); + root.appendChild(e); + } + // Store keys + QDomElement keys = doc.createElement("KeyDefinitions"); + root.appendChild(keys); + for (QValueVector<KTouchKey>::const_iterator it=m_keys.begin(); it!=m_keys.end(); ++it) + it->write(doc, keys); + // Store connectors + QDomElement conns = doc.createElement("KeyConnections"); + root.appendChild(conns); + for (QValueVector<KTouchKeyConnector>::const_iterator it=m_connectors.begin(); it!=m_connectors.end(); ++it) + it->write(doc, conns); +} +// ---------------------------------------------------------------------------- + +// Creates the default number keyboard. +void KTouchKeyboard::createDefault() { + // let's create a default keyboard + const int keySpacing = 4; + const int keyHeight = 20; + const int keyWidth = 20; + const int col = keyWidth+keySpacing; + const int row = keyHeight+keySpacing; + // First let's create the visible layout. + // This means we have to create all keys that will be displayed. + // Note: purely decorative keys get a key character code of 0! + m_keys.clear(); + m_keys.push_back( KTouchKey(KTouchKey::OTHER, 0, 0, 2*col+ 0, 0, keyWidth, keyHeight) ); + m_keys.back().m_otherKeyText = i18n("Num-lock", "Num"); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '/', 0, 2*col+ col, 0, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '*', 0, 2*col+ 2*col, 0, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '-', 0, 2*col+ 3*col, 0, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '7', 0, 2*col+ 0, row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '8', 0, 2*col+ 1*col, row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '9', 0, 2*col+ 2*col, row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::FINGER, '4', 0, 2*col+ 0, 2*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::FINGER, '5', 0, 2*col+ 1*col, 2*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::FINGER, '6', 0, 2*col+ 2*col, 2*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '1', 0, 2*col+ 0, 3*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '2', 0, 2*col+ 1*col, 3*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '3', 0, 2*col+ 2*col, 3*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '0', 0, 2*col+ 0, 4*row, 2*keyWidth+keySpacing, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::NORMAL, '.', 0, 2*col+ 2*col, 4*row, keyWidth, keyHeight) ); + m_keys.push_back( KTouchKey(KTouchKey::FINGER, '+', 0, 2*col+ 3*col, row, keyWidth, 2*keyHeight+keySpacing) ); + m_keys.push_back( KTouchKey(KTouchKey::ENTER, 0, 0, 2*col+ 3*col, 3*row, keyWidth, 2*keyHeight+keySpacing) ); + m_keys.push_back( KTouchKey(KTouchKey::BACKSPACE,0, 0, 2*col+ 5*col, 0, keyWidth, keyHeight) ); + + // now we need to create the connections between the characters that can be typed and the + // keys that need to be displayed on the keyboard + // The arguments to the constructor are: keychar, targetkey, fingerkey, controlkeyid + + m_connectors.clear(); + m_connectors.push_back( KTouchKeyConnector('/', '/','5', 0) ); + m_connectors.push_back( KTouchKeyConnector('*', '*','6', 0) ); + m_connectors.push_back( KTouchKeyConnector('-', '-','+', 0) ); + m_connectors.push_back( KTouchKeyConnector('+', '+', 0, 0) ); + m_connectors.push_back( KTouchKeyConnector('0', '0', 0, 0) ); + m_connectors.push_back( KTouchKeyConnector('1', '1','4', 0) ); + m_connectors.push_back( KTouchKeyConnector('2', '2','5', 0) ); + m_connectors.push_back( KTouchKeyConnector('3', '3','6', 0) ); + m_connectors.push_back( KTouchKeyConnector('4', '4', 0, 0) ); + m_connectors.push_back( KTouchKeyConnector('5', '5', 0, 0) ); + m_connectors.push_back( KTouchKeyConnector('6', '6', 0, 0) ); + m_connectors.push_back( KTouchKeyConnector('7', '7','4', 0) ); + m_connectors.push_back( KTouchKeyConnector('8', '8','5', 0) ); + m_connectors.push_back( KTouchKeyConnector('9', '9','6', 0) ); + m_connectors.push_back( KTouchKeyConnector('.', '.', '6', 0) ); + + m_title = "Number keypad"; + m_comment = "Predefined keyboard layout"; + m_language = QString::null; + // language does not apply to numbers... that's one of the nice things with math :-) + m_fontSuggestions = "Monospace"; + m_width = 8*col; + m_height = 5*row; +} +// ---------------------------------------------------------------------------- + +void KTouchKeyboard::updateConnections() { + for (QValueVector<KTouchKeyConnector>::iterator it = m_connectors.begin(); it != m_connectors.end(); ++it) + (*it).updateConnections(m_keys); +} +// ---------------------------------------------------------------------------- + + +/* + +bool KTouchKeyboard::loadKeyboard(QWidget * window, const KURL& url, QString* errorMsg) { + QString target; + if (KIO::NetAccess::download(url, target, window)) { + QString msg; + bool result = readKeyboard(target, msg); + KIO::NetAccess::removeTempFile(target); + if (!result && errorMsg!=NULL) + *errorMsg = i18n("Could not read the keyboard layout file '%1'. ").arg(url.url()) + msg; + return result; + } + else { + if (errorMsg!=NULL) + *errorMsg = i18n("Could not download/open keyboard layout file from '%1'.").arg(url.url()); + return false; + } +} + + +void KTouchKeyboard::saveKeyboard(QWidget * window, const KURL& url) { + QString tmpFile; + KTempFile *temp=0; + if (url.isLocalFile()) + tmpFile=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + tmpFile=temp->name(); + } + + QFile outfile(tmpFile); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + return; + } + + QTextStream out( &outfile ); + out << "########################################## \n"; + out << "# # \n"; + out << "# Keyboard layout file for KTouch # \n"; + out << "# # \n"; + out << "########################################## \n"; + out << "#\n"; + out << endl; + + for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) { + switch (key->type()) { + case KTouchKey::FINGER_KEY : out << "FingerKey "; break; + case KTouchKey::NORMAL_KEY : out << "NormalKey "; break; + case KTouchKey::CONTROL_KEY : out << "ControlKey "; break; + default : out << "NormalKey "; break; + } + QRect rect=key->frame(); + out << key->m_keyChar.unicode() << '\t' << key->m_keyText << '\t' + << rect.left() << '\t' << rect.top() << '\t' << rect.width() << '\t' << rect.height() << endl; + } + + if (temp) { + KIO::NetAccess::upload(tmpFile, url, window); + temp->unlink(); + delete temp; + } +} + +void KTouchKeyboard::applyPreferences(QWidget * window, bool silent) { + // let's check whether the keyboard layout has changed + if (KTouchConfig().m_currentKeyboardFile!=m_currentLayout) { + // if the layout is the number layout just create it and we're done + if (KTouchConfig().m_currentKeyboardFile=="number.keyboard") { + createDefaultKeyboard(); + resizeEvent(NULL); + return; + } + // ok, let's load this layout + if (silent) { + // during initialisation we don't want to have a message box, that's why this is silent + if (!loadKeyboard(window, KURL::fromPathOrURL( KTouchConfig().m_currentKeyboardFile ))) + createDefaultKeyboard(); + else + m_currentLayout=KTouchConfig().m_currentKeyboardFile; + } + else { + QString errorMsg; + if (!loadKeyboard(window, KURL::fromPathOrURL( KTouchConfig().m_currentKeyboardFile ), &errorMsg)) { + KMessageBox::error( 0, i18n("Error reading the keyboard layout; the default number keypad will " + "be created instead. You can choose another keyboard layout in the preferences dialog."), + errorMsg); + createDefaultKeyboard(); + } + else + m_currentLayout=KTouchConfig().m_currentKeyboardFile; + } + } + + updateColours(); // we recreate the colour connections, + resizeEvent(NULL); // paint the keyboard + newKey(m_nextKey); // and finally display the "next to be pressed" key again +} + + +void KTouchKeyboard::newKey(const QChar& nextChar) { + QPainter painter(this); + painter.translate(m_shift, MARGIN); + // first clean the markings on all keys + for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (key->m_isActive || key->m_isNextKey) { + key->m_isActive=key->m_isNextKey=false; + key->paint(painter); + } + } + + if (Prefs::showAnimation()){ // only do this if we want to show animation. + // find the key in the key connector list + QValueList<KTouchKeyConnector>::iterator keyIt = m_connectorList.begin(); + while (keyIt!=m_connectorList.end() && (*keyIt).m_keyChar!=nextChar) ++keyIt; + // if found mark the appropriate keys + if (keyIt!=m_connectorList.end()) { + QChar targetChar = (*keyIt).m_targetKeyChar; + QChar fingerChar = (*keyIt).m_fingerKeyChar; + QChar controlChar = (*keyIt).m_controlKeyChar; + // find the keys in the keylist + for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (key->m_keyChar==QChar(0)) continue; // skip decorative keys + if (key->m_keyChar==targetChar) key->m_isNextKey=true; + else if (key->m_keyChar==fingerChar) key->m_isActive=true; + else if (key->m_keyChar==controlChar) key->m_isActive=true; + if (key->m_isActive || key->m_isNextKey) + key->paint(painter); + } + } + m_nextKey = nextChar; + } +} + + +void KTouchKeyboard::paintEvent(QPaintEvent *) { + QPainter painter(this); + painter.translate(m_shift, MARGIN); + // just print all visible keys + for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) + key->paint(painter); +} + + +void KTouchKeyboard::resizeEvent(QResizeEvent *) { + double hScale = static_cast<double>(width()-2*MARGIN)/m_keyboardWidth; + double vScale = static_cast<double>(height()-2*MARGIN)/m_keyboardHeight; + double scale = std::min(hScale, vScale); + m_shift = (width() - static_cast<int>(m_keyboardWidth*scale))/2; + for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) + key->resize(scale); // resize all keys + update(); // and finally redraw the keyboard +} + + +void KTouchKeyboard::createDefaultKeyboard() { + // let's create a default keyboard + const int keySpacing = 4; + const int keyHeight = 20; + const int keyWidth = 20; + const int col = keyWidth+keySpacing; + const int row = keyHeight+keySpacing; + // first let's create the "visible" keys, that means all keys that will be displayed + // Note: purely decorative keys get a key char code of 0! + m_keyList.clear(); + m_keyList.append( new KTouchControlKey( 0, "Num", 0, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '/', "/", col, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '*', "*", 2*col, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '-', "-", 3*col, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '1', "1", 0, row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '2', "2", col, row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '3', "3", 2*col, row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '4', "4", 0, 2*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '5', "5", col, 2*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '6', "6", 2*col, 2*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '7', "7", 0, 3*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '8', "8", col, 3*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '9', "9", 2*col, 3*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '0', "0", 0, 4*row, 2*keyWidth+keySpacing, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '.', ".", 2*col, 4*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '+', "+", 3*col, row, keyWidth, 2*keyHeight+keySpacing) ); + m_keyList.append( new KTouchControlKey(13, "Enter", 3*col, 3*row,keyWidth, 2*keyHeight+keySpacing) ); + m_keyList.append( new KTouchControlKey(8, "BackSpace", 5*col, 0, 2*keyWidth+keySpacing, keyHeight) ); + m_keyboardWidth = 7*col; + m_keyboardHeight = 5*row; + // now we need to create the connections between the characters that can be typed and the + // keys that need to be displayed on the keyboard + // The arguments to the constructor are: keychar, targetkey, fingerkey, controlkey + m_connectorList.clear(); + m_connectorList.append( KTouchKeyConnector('/', '/', '5', 0) ); + m_connectorList.append( KTouchKeyConnector('*', '*', '6', 0) ); + m_connectorList.append( KTouchKeyConnector('-', '-', '+', 0) ); + m_connectorList.append( KTouchKeyConnector('+', '+', 0, 0) ); + m_connectorList.append( KTouchKeyConnector('.', '.', '6', 0) ); + m_connectorList.append( KTouchKeyConnector('1', '1', '4', 0) ); + m_connectorList.append( KTouchKeyConnector('2', '2', '5', 0) ); + m_connectorList.append( KTouchKeyConnector('3', '3', '6', 0) ); + m_connectorList.append( KTouchKeyConnector('4', '4', 0, 0) ); + m_connectorList.append( KTouchKeyConnector('5', '5', 0, 0) ); + m_connectorList.append( KTouchKeyConnector('6', '6', 0, 0) ); + m_connectorList.append( KTouchKeyConnector('7', '7', '4', 0) ); + m_connectorList.append( KTouchKeyConnector('8', '8', '5', 0) ); + m_connectorList.append( KTouchKeyConnector('9', '9', '6', 0) ); + m_connectorList.append( KTouchKeyConnector('0', '0', 0, 0) ); + m_connectorList.append( KTouchKeyConnector( 8, 8, 0, 0) ); + m_connectorList.append( KTouchKeyConnector( 13, 13, '+', 0) ); + updateColours(); + m_currentLayout="number"; +} + + +bool KTouchKeyboard::readKeyboard(const QString& fileName, QString& errorMsg) { + QFile infile(fileName); + if ( !infile.open( IO_ReadOnly ) ) { + errorMsg = i18n("Could not open file."); + return false; + } + QTextStream in( &infile ); + in.setEncoding(QTextStream::UnicodeUTF8); + QString line; + m_keyList.clear(); // empty the keyboard + m_connectorList.clear(); // clear the connections + m_keyboardWidth=0; + m_keyboardHeight=0; + // now loop until end of file is reached + do { + // skip all empty lines or lines containing a comment (starting with '#') + do { line = in.readLine().stripWhiteSpace(); } + while (!line.isNull() && (line.isEmpty() || line[0]=='#')); + // Check if end of file encountered and if that is the case -> bail out at next while + if (line.isNull()) continue; + + // 'line' should now contain a key specification + QTextStream lineStream(line, IO_ReadOnly); + QString keyType; + int keyAscII; + QString keyText; + int x(0), y(0), w(0), h(0); + lineStream >> keyType >> keyAscII; + if (keyType=="FingerKey") { + lineStream >> keyText >> x >> y >> w >> h; + if (w==0 || h==0) + w=h=8; // default values for old keyboard files + m_keyList.append( new KTouchFingerKey(keyAscII, keyText, x+1, y+1, w, h) ); + m_connectorList.append( KTouchKeyConnector(keyAscII, keyAscII, 0, 0) ); + } + else if (keyType=="ControlKey") { + lineStream >> keyText >> x >> y >> w >> h; + m_keyList.append( new KTouchControlKey(keyAscII, keyText, x+1, y+1, w-2, h-2) ); + m_connectorList.append( KTouchKeyConnector(keyAscII, keyAscII, 0, 0) ); + } + else if (keyType=="NormalKey") { + int fingerCharCode; + lineStream >> keyText >> x >> y >> fingerCharCode; + w=h=8; // default values for old keyboard files + // retrieve the finger key with the matching char + m_keyList.append( new KTouchNormalKey(keyAscII, keyText, x+1, y+1, w, h) ); + m_connectorList.append( KTouchKeyConnector(keyAscII, keyAscII, fingerCharCode, 0) ); + } else if (keyType=="HiddenKey") { + int targetChar, fingerChar, controlChar; + lineStream >> targetChar >> fingerChar >> controlChar; + m_connectorList.append( KTouchKeyConnector(keyAscII, targetChar, fingerChar, controlChar) ); + } + else { + errorMsg = i18n("Missing key type in line '%1'.").arg(line); + return false; + } + // calculate the maximum extent of the keyboard on the fly... + m_keyboardWidth = std::max(m_keyboardWidth, x+w); + m_keyboardHeight = std::max(m_keyboardHeight, y+h); + } while (!in.atEnd() && !line.isNull()); + updateColours(); + return (!m_keyList.isEmpty()); // empty file means error +} + + +void KTouchKeyboard::updateColours() { + // loop over all key connections + for (QValueList<KTouchKeyConnector>::iterator it = m_connectorList.begin(); it!=m_connectorList.end(); ++it) { + QChar fingerChar = (*it).m_fingerKeyChar; + if (fingerChar == QChar(0)) continue; + QChar targetChar = (*it).m_targetKeyChar; + KTouchKey * self=NULL; + KTouchKey * colorSource=NULL; + // loop over all keys to find the key pointers + for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (key->m_keyChar==targetChar) self=key; + else if (key->m_keyChar==fingerChar) colorSource=key; + } + if (self && colorSource) { + if (self->type()!=KTouchKey::NORMAL_KEY) + continue; + KTouchNormalKey *nk = dynamic_cast<KTouchNormalKey*>(self); + if (colorSource->type()!=KTouchKey::FINGER_KEY) { + kdDebug() << "[KTouchKeyboard::updateColours] Colour source key '" << colorSource->m_keyText + << "' is not a finger key!" << endl; + if (nk) { + nk->m_colorIndex = 0; + } + continue; + } + if (nk) { + KTouchFingerKey *fk = dynamic_cast<KTouchFingerKey*>(colorSource); + if (fk) { + nk->m_colorIndex = fk->m_colorIndex; + } + } + } + } +} + +*/ + diff --git a/ktouch/src/ktouchkeyboard.h b/ktouch/src/ktouchkeyboard.h new file mode 100644 index 00000000..88949537 --- /dev/null +++ b/ktouch/src/ktouchkeyboard.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * ktouchkeyboard.h * + * ---------------- * + * Copyright (C) 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. * + ***************************************************************************/ + +#ifndef KTOUCHKEYBOARD_H +#define KTOUCHKEYBOARD_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qvaluevector.h> + +#include "ktouchkeyconnector.h" +#include "ktouchkey.h" + +class KURL; + +/// This class stores the keyboard layout and the connectivity between characters +/// and the actual keys. +class KTouchKeyboard { + public: + /// Default constructor, sets up the standard number keyboard. + KTouchKeyboard() { createDefault(); } + /// Clears the keyboard (resets all data) + void clear(); + /// Loads a keyboard layout (old format) from file (returns true if successful). + bool load(QWidget * window, const KURL& url); + /// Loads a lecture (in XML format) from file (returns true if successful). + bool loadXML(QWidget * window, const KURL& url); + /// Saves the lecture data to file (returns true if successful). + bool saveXML(QWidget * window, const KURL& url) const; + /// Creates the default number keyboard. + void createDefault(); + /// Updates the indices in the KTouchKeyConnector objects for faster access. + void updateConnections(); + + QValueVector<KTouchKey> m_keys; ///< Vector with key definitions. + QValueVector<KTouchKeyConnector> m_connectors; ///< Vector with connectivity data. + + QString m_title; ///< Title of the keyboard (to appear in the menu). + QString m_comment; ///< Comments about the creator of the keyboard layout. + QString m_language; ///< Language ID of keyboard + QString m_fontSuggestions; ///< Suggestions of fonts to be used on the keys. + + // These variables are recalculated after the keyboard was loaded. + int m_width; ///< The width of the keyboard (maximum of the sums of all keywidths in each line). + int m_height; ///< The height of the keyboard (sum of all key row heights). + + private: + /// Loads keyboard data from file + bool read(QTextStream& in); + /// Loads keyboard data from file into an XML document + bool read(const QDomDocument& doc); + /// Saves keyboard data in the XML document + void write(QDomDocument& doc) const; +}; + +#endif // KTOUCHKEYBOARD_H diff --git a/ktouch/src/ktouchkeyboardeditor.cpp b/ktouch/src/ktouchkeyboardeditor.cpp new file mode 100644 index 00000000..c3aa93d2 --- /dev/null +++ b/ktouch/src/ktouchkeyboardeditor.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * ktouchkeyboardeditor.cpp * + * ------------------------ * + * Copyright (C) 2000 by Håvard Frøiland, 2004 by Andreas Nicolai * + * haavard@users.sourceforge.net, 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 "ktouchkeyboardeditor.h" +#include "ktouchkeyboardeditor.moc" + +#include <qlabel.h> +#include <qfont.h> + +#include <kmessagebox.h> +#include <kfiledialog.h> +#include <klocale.h> +#include <ksqueezedtextlabel.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <kfontdialog.h> +#include <kdebug.h> + +#include <utility> + +#include "ktouch.h" +#include "ktouchopenrequest.h" + +// ************************** +// ***** Public functions *** +// ************************** + +KTouchKeyboardEditor::KTouchKeyboardEditor(QWidget* parent, const char* name, bool modal, WFlags fl) + : KTouchKeyboardEditorDlg(parent,name, modal,fl) +{ +} +// ----------------------------------------------------------------------------- + +bool KTouchKeyboardEditor::startEditor(const KURL& url) { + // call open request dialog and load a keyboard and start the dialogs event loop if + // the user did not cancel the open request dialog + if (openKeyboardFile(url)==QDialog::Accepted) { + exec(); + return true; + } + else return false; +} +// ----------------------------------------------------------------------------- + + +// ************************ +// ***** Public slots ***** +// ************************ + + +void KTouchKeyboardEditor::fontBtnClicked() { + //kdDebug() << "Fontbutton clicked" << endl; + QFont f; + if (KFontDialog::getFont(f, false, this, true)==QDialog::Accepted) { + m_keyboard.m_fontSuggestions = f.toString(); + // update font and keyboard display + titleEdit->setFont(f); + keyboardCommentEdit->setFont(f); + languageEdit->setFont(f); + update(); // trigger repaint of the keyboard. + setModified(); + } +} +// ----------------------------------------------------------------------------- + +void KTouchKeyboardEditor::openBtnClicked() { + saveModified(); // save if modified + openKeyboardFile(""); +} +// ----------------------------------------------------------------------------- + +void KTouchKeyboardEditor::saveBtnClicked() { + if (m_currentURL.isEmpty()) saveAsBtnClicked(); + else { + transfer_from_dialog(); + //m_keyboard.saveXML(this, m_currentURL); + setModified(false); + } +} +// ----------------------------------------------------------------------------- + +void KTouchKeyboardEditor::saveAsBtnClicked() { + QString tmp = KFileDialog::getSaveFileName(QString::null, + i18n("*.keyboard.xml|KTouch Keyboard Files (*.keyboard.xml)\n*.*|All Files"), this, i18n("Save Keyboard Layout") ); + if (!tmp.isEmpty()) { + transfer_from_dialog(); + m_currentURL = tmp; + //m_keyboard.saveXML(this, m_currentURL); + setModified(false); + } +} +// ----------------------------------------------------------------------------- + +/// Called when the "Add..." button was clicked +void KTouchKeyboardEditor::addBtnClicked() { +} +// ----------------------------------------------------------------------------- + +/// Called when the "Edit..." button was clicked +void KTouchKeyboardEditor::editBtnClicked() { +} +// ----------------------------------------------------------------------------- + +/// Called when the "Remove" button was clicked +void KTouchKeyboardEditor::removeBtnClicked() { +} +// ----------------------------------------------------------------------------- + +/// Called when the selection in the key list box has changed +void KTouchKeyboardEditor::keySelectionChanged(QListBoxItem * item) { +} +// ----------------------------------------------------------------------------- + + + +void KTouchKeyboardEditor::paintEvent(QPaintEvent *) { + +} +// ----------------------------------------------------------------------------- + +// **************************** +// ***** Private functions **** +// **************************** + +void KTouchKeyboardEditor::transfer_to_dialog() { + if (m_currentURL.isEmpty()) { + titleEdit->setText( i18n("untitled keyboard layout") ); + keyboardCommentEdit->setText(""); + } + else { + titleEdit->setText(m_keyboard.m_title); + keyboardCommentEdit->setText(m_keyboard.m_comment); + } + languageEdit->setText(m_keyboard.m_language); + kdDebug() << "Setting font '"<< m_keyboard.m_fontSuggestions <<"'" << endl; + if (!m_keyboard.m_fontSuggestions.isEmpty()) { + QFont f; + f.fromString(m_keyboard.m_fontSuggestions); + titleEdit->setFont(f); + keyboardCommentEdit->setFont(f); + languageEdit->setFont(f); + } + kdDebug() << "Adding key definitions to key list" << endl; + keyListBox->clear(); + QValueVector<KTouchKey>::iterator it; + unsigned int min_x = 100000; + unsigned int max_x = 0; + unsigned int min_y = 100000; + unsigned int max_y = 0; + for( it = m_keyboard.m_keys.begin(); it != m_keyboard.m_keys.end(); ++it ) { + switch (it->m_type) { + case KTouchKey::NORMAL : keyListBox->insertItem("N '" + QString(it->m_primaryChar) + "'"); break; + case KTouchKey::FINGER : keyListBox->insertItem("F '" + QString(it->m_primaryChar) + "'"); break; + default : keyListBox->insertItem("O '" + it->m_otherKeyText + "'"); break; + } + min_x = std::min<unsigned int>(min_x, it->m_x); + max_x = std::max<unsigned int>(max_x, it->m_x+it->m_w); + min_y = std::min<unsigned int>(min_y, it->m_y); + max_y = std::max<unsigned int>(max_y, it->m_y+it->m_h); + } + dimensionsLabel->setText( i18n("Keyboard dimensions: %1 x %2").arg(max_x - min_x).arg(max_y - min_y) ); +} +// ----------------------------------------------------------------------------- + +void KTouchKeyboardEditor::transfer_from_dialog() { + m_keyboard.m_title = titleEdit->text(); + if (m_keyboard.m_title.isEmpty()) m_keyboard.m_title = i18n("untitled keyboard layout"); + m_keyboard.m_comment = keyboardCommentEdit->text(); + m_keyboard.m_language = languageEdit->text(); +} +// ----------------------------------------------------------------------------- + +int KTouchKeyboardEditor::openKeyboardFile(const KURL& url) { + // First setup the open request dialog + KTouchOpenRequest dlg(this); + // Call the dialog + KURL new_url; + int result = dlg.requestFileToOpen(new_url, + i18n("Open keyboard file ..."), + i18n("Which keyboard file would you like to edit?"), + i18n("Edit current keyboard:"), + i18n("Open a default keyboard:"), + i18n("Open a keyboard file:"), + i18n("Create new keyboard!"), + url, KTouchPtr->lectureFiles(), i18n("<no keyboard files available>")); + + if (result == QDialog::Accepted) { + // Ok, user confirmed the dialog, now lets get the url + m_currentURL = new_url; + // Try to load the keyboard, if that fails, we create a new keyboard instead + if (!m_currentURL.isEmpty() && + !m_keyboard.load(this, m_currentURL) && !m_keyboard.loadXML(this, m_currentURL)) + { + KMessageBox::sorry(this, i18n("Could not open the keyboard file, creating a new one instead!")); + m_currentURL = QString::null; // new keyboards haven't got a URL + m_keyboard.clear(); + } + // If we have no URL, we create a new keyboard - can happen if either the user + // chose "new keyboard" or the chosen keyboard could not be opened + if (m_currentURL.isEmpty()) { + m_keyboard.clear(); + setModified(true); // new keyboards are modified by default + } + else + setModified(false); // newly read keyboards are not modified in the begin + transfer_to_dialog(); // Update our editor with the keyboard data + return QDialog::Accepted; + } + else return QDialog::Rejected; +} +// ----------------------------------------------------------------------------- + +void KTouchKeyboardEditor::setModified(bool flag) { + m_modified = flag; + if (!m_currentURL.isEmpty()) { + if (flag) this->setCaption("KTouch Keyboard Editor - " + m_currentURL.url() + i18n(" (modified)")); + else this->setCaption("KTouch Keyboard Editor - " + m_currentURL.url()); + } + else + this->setCaption("KTouch Keyboard Editor - " + i18n("<unnamed keyboard file>")); +} +// ----------------------------------------------------------------------------- + +bool KTouchKeyboardEditor::saveModified() { + if (!m_modified) return true; + // ok, ask the user to save the changes + int result = KMessageBox::questionYesNoCancel(this, + i18n("The keyboard has been changed. Do you want to save the changes?"),QString::null,KStdGuiItem::save(),KStdGuiItem::discard()); + if (result == KMessageBox::Cancel) return false; // User aborted + if (result == KMessageBox::Yes) saveBtnClicked(); + // if successfully saved the modified flag will be resetted in the saveBtnClicked() function + return true; // User acknowledged +} +// ----------------------------------------------------------------------------- diff --git a/ktouch/src/ktouchkeyboardeditor.h b/ktouch/src/ktouchkeyboardeditor.h new file mode 100644 index 00000000..2c42a3a9 --- /dev/null +++ b/ktouch/src/ktouchkeyboardeditor.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * ktouchkeyboardeditor.h * + * ---------------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2004 by Andreas Nicolai * + * haavard@users.sourceforge.net, 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. * + ***************************************************************************/ + +#ifndef KTOUCHKEYBOARDEDITOR_H +#define KTOUCHKEYBOARDEDITOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ktouchkeyboardwidget.h" +#include "ktouchkeyboardeditor_dlg.h" + +#include "ktouchkeyboard.h" + +#include <kurl.h> + +/// This is the KTouch keyboard editor dialog. +/// +/// The keyboard edit dialog works like a standalone edit tool for editing keyboards. +/// Upon opening of the dialog the 'open keyboard file' dialog box is shown where +/// the user can either select a local keyboard file or load a keyboard file from +/// somewhere. Then the user can edit the keyboard and save it (if desired under a +/// different file name). +/// The use of the editor is simple, just include ktouchkeyboardeditor.h and do +/// @code +/// KTouchKeyboardEditor dlg; +/// bool ok = dlg.startEditor(current_keyboard); +/// // current_keyboard should be the URL pointing to the current/default +/// // keyboard or an empty URL +/// @endcode +/// @see startEditor() for details on the return value. +class KTouchKeyboardEditor : public KTouchKeyboardEditorDlg { + Q_OBJECT + + public: + /// Constructor. + KTouchKeyboardEditor(QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 ); + /// Default destructor. + ~KTouchKeyboardEditor() {} + /// Shows and executes the dialog (argument is the url to the default or + /// current keyboard file). + /// @return Returns 'true' if the dialog was properly executed (thus reloading + /// of the keyboard in KTouch is necessary) or 'false' if user canceled + /// the "Open request" dialog. + bool startEditor(const KURL& url); + + public slots: + /// Called when the Font button was clicked. + virtual void fontBtnClicked(); + /// Called when the Open button was clicked. + virtual void openBtnClicked(); + /// Called when the Save button was clicked. + virtual void saveBtnClicked(); + /// Called when the Save As button was clicked. + virtual void saveAsBtnClicked(); + /// Called when the editor is closed. + virtual void closeQuery() { if (saveModified()) accept(); }; + /// Called when the "Add..." button was clicked + virtual void addBtnClicked(); + /// Called when the "Edit..." button was clicked + virtual void editBtnClicked(); + /// Called when the "Remove" button was clicked + virtual void removeBtnClicked(); + /// Called when the selection in the key list box has changed + virtual void keySelectionChanged(QListBoxItem * item); + /// Will be called whenever some changes are made. + void setModified() { setModified(true); } + + protected slots: + /// Called when the editor is rejected (x clicked). + void reject() { closeQuery(); }; + + + protected: + /// Draws the keyboard in the keyboardPreviewLabel. + void paintEvent(QPaintEvent *); + + private: + /// Transfers data from the keyboard object to the dialog. + void transfer_to_dialog(); + /// Transfers data from the dialog to the keyboard object. + void transfer_from_dialog(); + /// Setups and opens the 'Open request dialog'. The url 'url' is used in the + /// open request dialog for the 'current' url selection. When the open request + /// dialog is closed the function attempts to load the keyboard or if that failes + /// creates a new, empty keyboard. + /// @return The function returns the return code from the open request dialog, so + /// that one can react on a "cancel" choice. + int openKeyboardFile(const KURL& url); + /// Changes the state of the lecture file (flag==true means modified). + void setModified(bool flag); + /// If the keyboard is modified the user is prompted to save it or throw away the changes. + /// If the user accepts the dialog the file is saved. + /// @return The function returns 'false' if the user aborted the save request, otherwise true. + bool saveModified(); + + KTouchKeyboard m_keyboard; ///< The keyboard data. + bool m_selecting; ///< Flag to prevent the selection slot from being selected twice. + bool m_modified; ///< Flag indicating whether the keyboard has been modified. + KURL m_currentURL; ///< URL of the current keyboard. +}; + +#endif // KTOUCHKEYBOARDEDITOR_H diff --git a/ktouch/src/ktouchkeyboardeditor_dlg.ui b/ktouch/src/ktouchkeyboardeditor_dlg.ui new file mode 100644 index 00000000..f6aa1009 --- /dev/null +++ b/ktouch/src/ktouchkeyboardeditor_dlg.ui @@ -0,0 +1,406 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchKeyboardEditorDlg</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KTouchKeyboardEditorDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>779</width> + <height>545</height> + </rect> + </property> + <property name="caption"> + <string>Keyboard Layout Editor</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>fontButton</cstring> + </property> + <property name="text"> + <string>Change Font...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>openButton</cstring> + </property> + <property name="text"> + <string>Open...</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>saveButton</cstring> + </property> + <property name="text"> + <string>Save</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>saveAsButton</cstring> + </property> + <property name="text"> + <string>Save As...</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>quitButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Keyboard Layout</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>dimensionsLabel</cstring> + </property> + <property name="text"> + <string>Keyboard dimensions:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>keyboardPreviewLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>100</height> + </size> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="0" column="1" rowspan="2" colspan="1"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Key Definitions</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListBox"> + <property name="name"> + <cstring>keyListBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Add...</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>editButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Edit...</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>removeButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Key definition data:</string> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>keyInfoText</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Keyboard Identification Data</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>titleLabel</cstring> + </property> + <property name="font"> + <font> + <family>Helvetica</family> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Keyboard title:</string> + </property> + </widget> + <widget class="KLineEdit" row="0" column="2"> + <property name="name"> + <cstring>titleEdit</cstring> + </property> + </widget> + <widget class="KTextEdit" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>keyboardCommentEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>70</height> + </size> + </property> + </widget> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Comments:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Language id:</string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>languageEdit</cstring> + </property> + </widget> + </grid> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>fontButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>fontBtnClicked()</slot> + </connection> + <connection> + <sender>openButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>openBtnClicked()</slot> + </connection> + <connection> + <sender>saveAsButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>saveAsBtnClicked()</slot> + </connection> + <connection> + <sender>saveButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>saveBtnClicked()</slot> + </connection> + <connection> + <sender>quitButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>closeQuery()</slot> + </connection> + <connection> + <sender>addButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>addBtnClicked()</slot> + </connection> + <connection> + <sender>editButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>editBtnClicked()</slot> + </connection> + <connection> + <sender>removeButton</sender> + <signal>clicked()</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>removeBtnClicked()</slot> + </connection> + <connection> + <sender>keyListBox</sender> + <signal>selectionChanged(QListBoxItem*)</signal> + <receiver>KTouchKeyboardEditorDlg</receiver> + <slot>keySelectionChanged(QListBoxItem*)</slot> + </connection> +</connections> +<slots> + <slot>openBtnClicked()</slot> + <slot>saveBtnClicked()</slot> + <slot>saveAsBtnClicked()</slot> + <slot>closeQuery()</slot> + <slot>fontBtnClicked()</slot> + <slot>addBtnClicked()</slot> + <slot>editBtnClicked()</slot> + <slot>removeBtnClicked()</slot> + <slot>keySelectionChanged(QListBoxItem*)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>ktextedit.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchkeyboardwidget.cpp b/ktouch/src/ktouchkeyboardwidget.cpp new file mode 100644 index 00000000..6e92c99d --- /dev/null +++ b/ktouch/src/ktouchkeyboardwidget.cpp @@ -0,0 +1,532 @@ +/*************************************************************************** + * ktouchkeyboardwidget.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 "ktouchkeyboardwidget.h" +#include "ktouchkeyboardwidget.moc" + +#include <algorithm> +#include <set> + +#include <qfile.h> + +#include <kdebug.h> +#include <ktempfile.h> +#include <klocale.h> +#include <kio/netaccess.h> +#include <kstandarddirs.h> +#include <kmessagebox.h> + +#include "prefs.h" +#include "ktouchkeyconnector.h" + +// the margin between keyboard and widget frame +const int MARGIN = 10; + +QMap<QChar, int> KTouchKeyboardWidget::m_keyCharMap; +// -------------------------------------------------------------------------- + + +KTouchKeyboardWidget::KTouchKeyboardWidget(QWidget *parent) + : QWidget(parent), m_keyboardWidth(100), m_keyboardHeight(60), m_currentLayout(""), + m_hideKeyboard(false) +{ + setMinimumHeight(100); // when it's smaller you won't see anything + m_keyList.setAutoDelete(true); // the list is responsable for cleaning up +} + + +bool KTouchKeyboardWidget::loadKeyboard(QWidget * window, const KURL& url, QString* errorMsg) { + QString target; + if (KIO::NetAccess::download(url, target, window)) { + QString msg; + bool result = readKeyboard(target, msg); + KIO::NetAccess::removeTempFile(target); + if (!result && errorMsg!=NULL) + *errorMsg = i18n("Could not read the keyboard layout file '%1'. ").arg(url.url()) + msg; + return result; + } + else { + if (errorMsg!=NULL) + *errorMsg = i18n("Could not download/open keyboard layout file from '%1'.").arg(url.url()); + return false; + } +} + + +void KTouchKeyboardWidget::saveKeyboard(QWidget * window, const KURL& url) { + QString tmpFile; + KTempFile *temp=0; + if (url.isLocalFile()) + tmpFile=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + tmpFile=temp->name(); + } + + QFile outfile(tmpFile); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + return; + } + + QTextStream out( &outfile ); + out << "########################################## \n"; + out << "# # \n"; + out << "# Keyboard layout file for KTouch # \n"; + out << "# # \n"; + out << "########################################## \n"; + out << "#\n"; + out << endl; + + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) { + switch (key->type()) { + case KTouchBaseKey::FINGER_KEY : out << "FingerKey "; break; + case KTouchBaseKey::NORMAL_KEY : out << "NormalKey "; break; + case KTouchBaseKey::CONTROL_KEY : out << "ControlKey "; break; + default : out << "NormalKey "; break; + } + QRect rect=key->frame(); + out << key->m_keyChar.unicode() << '\t' << key->m_keyText << '\t' + << rect.left() << '\t' << rect.top() << '\t' << rect.width() << '\t' << rect.height() << endl; + } + + if (temp) { + KIO::NetAccess::upload(tmpFile, url, window); + temp->unlink(); + delete temp; + } +} + +void KTouchKeyboardWidget::applyPreferences(QWidget * window, bool silent) { + m_hideKeyboard = Prefs::hideKeyboard(); + if (m_hideKeyboard) + setMaximumHeight(100); + else + setMaximumHeight(10000); + // let's check whether the keyboard layout has changed + if (Prefs::currentKeyboardFile() != m_currentLayout) { + // if the layout is the number layout just create it and we're done + //kdDebug() << "[KTouchKeyboardWidget::applyPreferences] keyboard = " << Prefs::currentKeyboardFile() << endl; + if (Prefs::currentKeyboardFile()=="number.keyboard") { + createDefaultKeyboard(); + resizeEvent(NULL); + return; + } + // ok, let's load this layout + if (silent) { + // during initialisation we don't want to have a message box, that's why this is silent + if (!loadKeyboard(window, KURL::fromPathOrURL( Prefs::currentKeyboardFile() ))) + createDefaultKeyboard(); + else + m_currentLayout=Prefs::currentKeyboardFile(); + } + else { + QString errorMsg; + if (!loadKeyboard(window, KURL::fromPathOrURL( Prefs::currentKeyboardFile() ), &errorMsg)) { + KMessageBox::error( 0, i18n("Error reading the keyboard layout; the default number keypad will " + "be created instead. You can choose another keyboard layout in the preferences dialog."), + errorMsg); + createDefaultKeyboard(); + } + else + m_currentLayout=Prefs::currentKeyboardFile(); + } + } + updateColours(); // we recreate the colour connections + // assign keyboard font to keys + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (Prefs::overrideKeyboardFont()) + key->m_font = Prefs::keyboardFont(); + else + key->m_font = Prefs::font(); + } + // kdDebug() << "[KTouchKeyboard::applyPreferences] Assigned key font" << endl; + resizeEvent(NULL); // paint the keyboard + newKey(m_nextKey); // and finally display the "next to be pressed" key again +} + + +void KTouchKeyboardWidget::newKey(const QChar& nextChar) { + if (m_hideKeyboard) return; + QPainter painter(this); + painter.translate(m_shift, MARGIN); + // first clean the markings on all keys + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (key->m_isActive || key->m_isNextKey) { + key->m_isActive=key->m_isNextKey=false; + key->paint(painter); + } + } + + if (Prefs::showAnimation()){ // only do this if we want to show animation. + // find the key in the key connector list + QValueList<KTouchKeyConnection>::iterator keyIt = m_connectorList.begin(); + while (keyIt!=m_connectorList.end() && (*keyIt).m_keyChar!=nextChar) ++keyIt; + // if found mark the appropriate keys + if (keyIt!=m_connectorList.end()) { + QChar targetChar = (*keyIt).m_targetKeyChar; + QChar fingerChar = (*keyIt).m_fingerKeyChar; + QChar controlChar = (*keyIt).m_controlKeyChar; + // find the keys in the keylist + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (key->m_keyChar==QChar(0)) continue; // skip decorative keys + if (key->m_keyChar==targetChar) key->m_isNextKey=true; + else if (key->m_keyChar==fingerChar) key->m_isActive=true; + else if (key->m_keyChar==controlChar) key->m_isActive=true; + if (key->m_isActive || key->m_isNextKey) + key->paint(painter); + } + } + m_nextKey = nextChar; + } +} + + +void KTouchKeyboardWidget::paintEvent(QPaintEvent *) { + if (m_hideKeyboard) return; + QPainter p(this); + p.translate(m_shift, MARGIN); + // just print all visible keys + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) + key->paint(p); + +/* // TODO : later + + const KTouchColorScheme& colorScheme = KTouchColorScheme::m_colorSchemes[Prefs::colorScheme()]; + for (QValueVector<KTouchKey>::iterator it = m_keys.begin(); it != m_keys.end(); ++it) { + // determine colors + QColor textColor; + if (it->m_type == KTouchKey::NORMAL || it->m_type == KTouchKey::FINGER) { + if (is_next_key) { + // mark the key as "next" + p.setBrush( colorScheme.m_backgroundH ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_textH; + } + else { + p.setBrush( colorScheme.m_background[m_colorIndex] ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_text; + }; + } + else { + p.setBrush( colorScheme.m_cBackground ); + p.setPen( colorScheme.m_cText ); + } + + p.setPen(textColor); + p.fillRect(m_xS, m_xS, m_xS, m_xS, p.brush()); + p.drawRect(m_xS, m_xS, m_xS, m_xS); + + p.setFont( m_font ); + p.drawText(it->m_xS, it->m_yS, it->m_wS, it->m_hS, + QPainter::AlignCenter, m_keyText); + + } +*/ + // TODO : later copy pre-rendered and pre-scaled characters to screen +} + +void KTouchKeyboardWidget::resizeEvent(QResizeEvent *) { + // kdDebug() << "[KTouchKeyboard::resizeEvent] Window = " << width() << "x" << height() << endl; + // kdDebug() << "[KTouchKeyboard::resizeEvent] Keyboard = " << m_keyboardWidth << "x" << m_keyboardHeight << endl; + double hScale = static_cast<double>(width()-2*MARGIN)/m_keyboardWidth; + double vScale = static_cast<double>(height()-2*MARGIN)/m_keyboardHeight; + double scale = std::max(1.0, std::min(hScale, vScale)); + // kdDebug() << "[KTouchKeyboard::resizeEvent] using scale = " << scale << endl; + m_shift = (width() - static_cast<int>(m_keyboardWidth*scale))/2; + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) + key->resize(scale); // resize all keys + for (QValueVector<KTouchKey>::iterator it = m_keys.begin(); it != m_keys.end(); ++it) { + it->resize(scale); // resize all keys + } + update(); // and finally redraw the keyboard +} + + +void KTouchKeyboardWidget::createDefaultKeyboard() { + // let's create a default keyboard + int keySpacing = 4; + int keyHeight = 20; + int keyWidth = 20; + int col = keyWidth+keySpacing; + int row = keyHeight+keySpacing; + // first let's create the "visible" keys, that means all keys that will be displayed + // Note: purely decorative keys get a key char code of 0! + m_keyList.clear(); + m_keyList.append( new KTouchControlKey( 0, "Num", 0, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '/', "/", col, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '*', "*", 2*col, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '-', "-", 3*col, 0, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '7', "7", 0, row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '8', "8", col, row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '9', "9", 2*col, row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '4', "4", 0, 2*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '5', "5", col, 2*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '6', "6", 2*col, 2*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '1', "1", 0, 3*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '2', "2", col, 3*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '3', "3", 2*col, 3*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '0', "0", 0, 4*row, 2*keyWidth+keySpacing, keyHeight) ); + m_keyList.append( new KTouchNormalKey( '.', ".", 2*col, 4*row, keyWidth, keyHeight) ); + m_keyList.append( new KTouchFingerKey( '+', "+", 3*col, row, keyWidth, 2*keyHeight+keySpacing) ); + m_keyList.append( new KTouchControlKey(13, "Enter", 3*col, 3*row,keyWidth, 2*keyHeight+keySpacing) ); + m_keyList.append( new KTouchControlKey(8, "BackSpace", 5*col, 0, 2*keyWidth+keySpacing, keyHeight) ); + m_keyboardWidth = 7*col; + m_keyboardHeight = 5*row; + + // now we need to create the connections between the characters that can be typed and the + // keys that need to be displayed on the keyboard + // The arguments to the constructor are: keychar, targetkey, fingerkey, controlkey + m_connectorList.clear(); + m_connectorList.append( KTouchKeyConnection('/', '/', '5', 0) ); + m_connectorList.append( KTouchKeyConnection('*', '*', '6', 0) ); + m_connectorList.append( KTouchKeyConnection('-', '-', '+', 0) ); + m_connectorList.append( KTouchKeyConnection('+', '+', 0, 0) ); + m_connectorList.append( KTouchKeyConnection('.', '.', '6', 0) ); + m_connectorList.append( KTouchKeyConnection(',', '.', '6', 0) ); + m_connectorList.append( KTouchKeyConnection('7', '7', '4', 0) ); + m_connectorList.append( KTouchKeyConnection('8', '8', '5', 0) ); + m_connectorList.append( KTouchKeyConnection('9', '9', '6', 0) ); + m_connectorList.append( KTouchKeyConnection('4', '4', 0, 0) ); + m_connectorList.append( KTouchKeyConnection('5', '5', 0, 0) ); + m_connectorList.append( KTouchKeyConnection('6', '6', 0, 0) ); + m_connectorList.append( KTouchKeyConnection('1', '1', '4', 0) ); + m_connectorList.append( KTouchKeyConnection('2', '2', '5', 0) ); + m_connectorList.append( KTouchKeyConnection('3', '3', '6', 0) ); + m_connectorList.append( KTouchKeyConnection('0', '0', 0, 0) ); + m_connectorList.append( KTouchKeyConnection( 8, 8, 0, 0) ); + m_connectorList.append( KTouchKeyConnection( 13, 13, '+', 0) ); + updateColours(); + m_currentLayout="number.keyboard"; + + // create keyboard geometry for new keyboard data + + int sp = 10; + int h = 50; + int w = 50; + col = w+sp; + row = h+sp; + + m_keys.clear(); + m_keys.append( KTouchKey( 0, 0, w, h, QString("Num")) ); // 0 + m_keys.append( KTouchKey(KTouchKey::NORMAL, col, 0, w, h, '/') ); // 1 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 2*col, 0, w, h, '*') ); // 2 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 3*col, 0, w, h, '-') ); // 3 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 0, row, w, h, '7') ); // 4 + m_keys.append( KTouchKey(KTouchKey::NORMAL, col, row, w, h, '8') ); // 5 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 2*col, row, w, h, '9') ); // 6 + m_keys.append( KTouchKey(KTouchKey::FINGER, 0, 2*row, w, h, '4') ); // 7 *** + m_keys.append( KTouchKey(KTouchKey::FINGER, col, 2*row, w, h, '5') ); // 8 *** + m_keys.append( KTouchKey(KTouchKey::FINGER, 2*col, 2*row, w, h, '6') ); // 9 *** + m_keys.append( KTouchKey(KTouchKey::FINGER, 3*col, row, w, 2*h+sp, '+') ); // 10 *** + m_keys.append( KTouchKey(KTouchKey::NORMAL, 0, 3*row, w, h, '1') ); // 11 + m_keys.append( KTouchKey(KTouchKey::NORMAL, col, 3*row, w, h, '2') ); // 12 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 2*col, 3*row, w, h, '3') ); // 13 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 0, 4*row, 2*w+sp, h, '0') ); // 14 + m_keys.append( KTouchKey(KTouchKey::NORMAL, 2*col, 4*row, w, h, '.') ); // 15 + m_keys.append( KTouchKey(KTouchKey::ENTER, 3*col, 3*row, w, 2*h+sp, 0) ); // 16 + m_keys.append( KTouchKey(KTouchKey::BACKSPACE, 5*col, 0, 2*w+sp, h, 0) ); // 17 + // number the keys for reference + for (unsigned int i=0; i<m_keys.size(); ++i) + m_keys[i].m_number = i; + // create key connections + m_keyConnections.clear(); + // KTouchKeyConnector(char, target, finger, modifier) + m_keyConnections.append( KTouchKeyConnector('/', 1, 8, 0) ); + m_keyConnections.append( KTouchKeyConnector('*', 2, 9, 0) ); + m_keyConnections.append( KTouchKeyConnector('-', 3, 10, 0) ); + m_keyConnections.append( KTouchKeyConnector('7', 4, 7, 0) ); + m_keyConnections.append( KTouchKeyConnector('8', 5, 8, 0) ); + m_keyConnections.append( KTouchKeyConnector('9', 6, 9, 0) ); + m_keyConnections.append( KTouchKeyConnector('4', 7, 7, 0) ); // *** + m_keyConnections.append( KTouchKeyConnector('5', 8, 8, 0) ); // *** + m_keyConnections.append( KTouchKeyConnector('6', 9, 9, 0) ); // *** + m_keyConnections.append( KTouchKeyConnector('+', 10, 10, 0) ); // *** + m_keyConnections.append( KTouchKeyConnector('1', 11, 7, 0) ); + m_keyConnections.append( KTouchKeyConnector('2', 12, 8, 0) ); + m_keyConnections.append( KTouchKeyConnector('3', 13, 9, 0) ); + m_keyConnections.append( KTouchKeyConnector('0', 14, 7, 0) ); + m_keyConnections.append( KTouchKeyConnector('.', 15, 9, 0) ); + m_keyConnections.append( KTouchKeyConnector(QChar(13), 16, 10, 0) ); + m_keyConnections.append( KTouchKeyConnector(QChar(8), 17, 0, 0) ); +} + + +bool KTouchKeyboardWidget::readKeyboard(const QString& fileName, QString& errorMsg) { + QFile infile(fileName); + if ( !infile.open( IO_ReadOnly ) ) { + errorMsg = i18n("Could not open file."); + return false; + } + QTextStream in( &infile ); + in.setEncoding(QTextStream::UnicodeUTF8); + QString line; + m_keyList.clear(); // empty the keyboard + m_connectorList.clear(); // clear the connections + m_keyboardWidth=0; + m_keyboardHeight=0; + std::set<QChar> keys; + // now loop until end of file is reached + do { + // skip all empty lines or lines containing a comment (starting with '#') + do { line = in.readLine().stripWhiteSpace(); } + while (!line.isNull() && (line.isEmpty() || line[0]=='#')); + // Check if end of file encountered and if that is the case -> bail out at next while + if (line.isNull()) continue; + + // 'line' should now contain a key specification + QTextStream lineStream(line, IO_ReadOnly); + QString keyType; + int keyAscII; + QString keyText; + int x(0), y(0), w(0), h(0); + lineStream >> keyType >> keyAscII; + if (keyType=="FingerKey") { + lineStream >> keyText >> x >> y >> w >> h; + if (w==0 || h==0) + w=h=8; // default values for old keyboard files + m_keyList.append( new KTouchFingerKey(keyAscII, keyText, x+1, y+1, w, h) ); + m_connectorList.append( KTouchKeyConnection(keyAscII, keyAscII, 0, 0) ); +// kdDebug() << "read : F : unicode = " << keyAscII << " char = " << QChar(keyAscII) << endl; + } + else if (keyType=="ControlKey") { + lineStream >> keyText >> x >> y >> w >> h; + m_keyList.append( new KTouchControlKey(keyAscII, keyText, x+1, y+1, w-2, h-2) ); + m_connectorList.append( KTouchKeyConnection(keyAscII, keyAscII, 0, 0) ); +// kdDebug() << "read : C : unicode = " << keyAscII << " char = " << keyText << endl; + } + else if (keyType=="NormalKey") { + int fingerCharCode; + lineStream >> keyText >> x >> y >> fingerCharCode; + w=h=8; // default values for old keyboard files + // retrieve the finger key with the matching char + m_keyList.append( new KTouchNormalKey(keyAscII, keyText, x+1, y+1, w, h) ); + m_connectorList.append( KTouchKeyConnection(keyAscII, keyAscII, fingerCharCode, 0) ); +// kdDebug() << "read : N : unicode = " << keyAscII << " char = " << QChar(keyAscII) << endl; + } else if (keyType=="HiddenKey") { + int targetChar, fingerChar, controlChar; + lineStream >> targetChar >> fingerChar >> controlChar; + m_connectorList.append( KTouchKeyConnection(keyAscII, targetChar, fingerChar, controlChar) ); +// kdDebug() << "read : H : unicode = " << keyAscII << " char = " << QChar(keyAscII) << " target = " << targetChar << " finger = " << fingerChar << " control = " << controlChar << endl; + + } + else { + errorMsg = i18n("Missing key type in line '%1'.").arg(line); + return false; + } + if (keys.find(keyAscII)!=keys.end()) { + kdDebug() << "WARNING: Duplicate entry for char '"+keyText+"' with Unicode " << keyAscII << endl; + } + else + keys.insert(keyAscII); + + keys.insert(keyAscII); + + + // calculate the maximum extent of the keyboard on the fly... + m_keyboardWidth = std::max(m_keyboardWidth, x+w); + m_keyboardHeight = std::max(m_keyboardHeight, y+h); + } while (!in.atEnd() && !line.isNull()); +// kdDebug() << "showing all unicode numbers in this file" << endl; +/* for (std::set<QChar>::iterator it = keys.begin(); it != keys.end(); ++it) + kdDebug() << *it << endl; +*/ +// kdDebug() << "num chars = " << keys.size() << endl; +// kdDebug() << "num key connections = " << m_connectorList.size() << endl; + + updateColours(); + return (!m_keyList.isEmpty()); // empty file means error +} + + +void KTouchKeyboardWidget::updateColours() { +// kdDebug() << "KTouchKeyboardWidget::updateColours()" << endl; + // old functionality : loop over all key connections + m_keyCharMap.clear(); + unsigned int counter = 0; + for (QValueList<KTouchKeyConnection>::iterator it = m_connectorList.begin(); it!=m_connectorList.end(); ++it) { + // store finger and target characters + QChar fingerChar = (*it).m_fingerKeyChar; + QChar targetChar = (*it).m_targetKeyChar; // this is the _base_ character of the key that needs to be highlighted + QChar ch = (*it).m_keyChar; + +/* kdDebug() << "Key #"<<++counter<<": " << ch << "(" << ch.unicode() << ") " + << "target = " << targetChar << "(" << targetChar.unicode() << ") " + << "finger = " << fingerChar << "(" << fingerChar.unicode() << ")" << endl; +*/ + m_keyCharMap[ch] = -1; + + if (fingerChar == QChar(0)) { +// kdDebug() << "skipped char = " << targetChar << endl; + continue; // skips keys that don't have finger keys assigned + } + + KTouchBaseKey * self=NULL; + KTouchBaseKey * colorSource=NULL; + // loop over all keys to find the key pointers + for (KTouchBaseKey * key = m_keyList.first(); key; key = m_keyList.next()) { + if (key->m_keyChar==targetChar) self=key; + else if (key->m_keyChar==fingerChar) colorSource=key; + } + // ok, by now we should have found a finger and target char pointer + if (self && colorSource) { +// kdDebug() << "Key " << ++counter << " keychar = " << ch << " target = " << targetChar << " finger = " << fingerChar << endl; + // skip target keys that are actually control, finger or hidden keys + if (self->type()!=KTouchBaseKey::NORMAL_KEY) continue; + // try to downcast to get a normal key pointer + KTouchNormalKey *nk = dynamic_cast<KTouchNormalKey*>(self); + // skip if we couldn't downcast + if (!nk) { + kdDebug() << "Couldn't downcast to normal key for targer char = " << targetChar << endl; + continue; + } + // if our color source is not a valid finger key, skip with a warning + if (colorSource->type()!=KTouchBaseKey::FINGER_KEY) { + kdDebug() << "[KTouchKeyboard::updateColours] Colour source key '" << colorSource->m_keyText + << "' is not a finger key!" << endl; + continue; + } + // finally store the color code + KTouchFingerKey *fk = dynamic_cast<KTouchFingerKey*>(colorSource); + if (fk) { + nk->m_colorIndex = fk->m_colorIndex; + m_keyCharMap[ch] = fk->m_colorIndex; +// kdDebug() << "char = " << targetChar << " index = " << fk->m_colorIndex << endl; + } + } + } +// kdDebug() << "Colormap has " << m_keyCharMap.count() << " entries" << endl; + +/* + // new functionality + m_keyMap.clear(); + m_colorMap.clear(); + for (unsigned int i=0; i<m_keys.size(); ++i) + m_keyMap[i] = -1; + int c_index = 0; + for (QValueVector<KTouchKeyConnector>::iterator it = m_keyConnections.begin(); + it!=m_keyConnections.end(); ++it) + { + if (it->m_targetKeyIndex == -1) continue; + int n = it->m_targetKeyIndex; + switch (m_keys[n].m_type) { + case KTouchKey::NORMAL : m_keyMap[n] = it->m_fingerKeyIndex; break; + case KTouchKey::FINGER : m_keyMap[n] = it->m_fingerKeyIndex; + m_colorMap[n] = c_index++; + c_index %= 8; break; + default : ; + } + } +*/ +} + diff --git a/ktouch/src/ktouchkeyboardwidget.h b/ktouch/src/ktouchkeyboardwidget.h new file mode 100644 index 00000000..9ba71d8f --- /dev/null +++ b/ktouch/src/ktouchkeyboardwidget.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * ktouchkeyboardwidget.h * + * ---------------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2004 by Andreas Nicolai * + * haavard@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. * + ***************************************************************************/ + +#ifndef KTOUCHKEYBOARDWIDGET_H +#define KTOUCHKEYBOARDWIDGET_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qwidget.h> +#include <qvaluevector.h> +#include <qmap.h> +#include "ktouchkeys.h" + +#include "ktouchkey.h" // new class +#include "ktouchkeyconnector.h" // new class + +class KURL; + +/** This is the keyboard widget at the bottom of the training screen. + * In addition to the painting functions this class has the member + * functions loadKeyboard() and saveKeyboard() which can read and write + * a keyboard layout from/into a file.<p> + * The keyboard is drawn in the paintEvent() member function. The + * resizing, that means the recalculation of m_shift is + * done in the resizeEvent() member function.<p> + * The state of keyboard and keys does not change while it is + * repainted or resized. Only when a new character has been typed + * and the newKey() slot is called the state changes (and thus the + * activated keys and finger key animations etc.) + */ +class KTouchKeyboardWidget : public QWidget { + Q_OBJECT + public: + /// Constructor + KTouchKeyboardWidget(QWidget *parent); + /// Reads a keyboard layout from the given URL. + /// The function returns 'true' when the reading was successful or 'false' if not. In this + /// case the optional parameter errorMsg contains the error message. + bool loadKeyboard(QWidget * window, const KURL& url, QString* errorMsg=NULL); + /// Saves the keyboard layout to the URL. + void saveKeyboard(QWidget * window, const KURL& url); + /// Applies preferences to the keyboard layout and the keys. + /// This means that the layout is basically recreated and if the layout type/language + /// changed it will be reloaded. + void applyPreferences(QWidget * window, bool silent); + + public slots: + /// This function displays the next key (or key combination) the user has to press. + void newKey(const QChar& nextChar); + + protected: + /// Draws the keyboard. + void paintEvent(QPaintEvent *); + /// Resizes (recalculates m_shift and m_scale) and redraws the keyboard. + void resizeEvent(QResizeEvent *); + + private: + /// Does what the name says (create a default keyboard which is a number keypad). + /// This function is needed in case there no keyboard file could be found. + void createDefaultKeyboard(); + /// Does the actual reading. + bool readKeyboard(const QString& fileName, QString& errorMsg); + /// Assigns the background colours to the normal keys, which have a finger key assigned. + void updateColours(); + + // *** new data storage classes *** + QValueVector<KTouchKey> m_keys; ///< The geometrical key data. + QValueVector<KTouchKeyConnector> m_keyConnections; ///< Contains the character - key associations. + QMap<unsigned int, int> m_keyMap; ///< Links target keys with finger keys: m_keyMap[target_key] = finger_key + QMap<unsigned int, unsigned int> m_colorMap; ///< Links finger keys with color indices: m_colorMap[finger_key] = color_index in color scheme + + // *** old data storage classes *** + QPtrList<KTouchBaseKey> m_keyList; ///< The pointer list with base class pointers to the keys. + QValueList<KTouchKeyConnection> m_connectorList; ///< Contains the character - key associations. + + static QMap<QChar, int> m_keyCharMap; ///< Links target keys with finger keys: m_keyCharMap[target_key_char] = color_index + + int m_keyboardWidth; ///< The width of the keyboard (maximum of the sums of all keywidths in each line). + int m_keyboardHeight; ///< The height of the keyboard (sum of all key row heights). + double m_shift; ///< The horizontal shift for the keyboard. + QString m_currentLayout; ///< The name of the currently used layout. + QChar m_nextKey; ///< The next to be pressed character. + + bool m_hideKeyboard; ///< If true, the keyboard won't be shown. +}; + +#endif // KTOUCHKEYBOARDWIDGET_H diff --git a/ktouch/src/ktouchkeyconnector.cpp b/ktouch/src/ktouchkeyconnector.cpp new file mode 100644 index 00000000..2546400f --- /dev/null +++ b/ktouch/src/ktouchkeyconnector.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * ktouchkeyconnetor.cpp * + * --------------------- * + * Copyright (C) 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 "ktouchkeyconnector.h" + +// Reads the key connector data from the DomElement +bool KTouchKeyConnector::read(QDomNode node) { +/* QDomNode newChars = in.namedItem("NewCharacters"); + if (newChars.isNull()) m_newChars = i18n("basically all characters on the keyboard","abcdefghijklmnopqrstuvwxyz"); + else m_newChars = newChars.firstChild().nodeValue(); + QDomNode levelComment = in.namedItem("LevelComment"); + if (!levelComment.isNull()) m_comment = levelComment.firstChild().nodeValue(); + m_lines.clear(); + QDomNode line = in.namedItem("Line"); + while ( !line.isNull() ) { + m_lines.push_back( line.firstChild().nodeValue() ); + line = line.nextSibling(); + } + if (m_lines.empty()) { + createDefault(); + return false; + } +*/ + return true; +} + +// Creates a new DomElement, writes the key connector data into it and appends it to the root object. +void KTouchKeyConnector::write(QDomDocument& doc, QDomElement& root) const { +/* QDomElement element = doc.createElement("KeyConnector"); + QDomText textnode = doc.createTextNode(QString(m_keyChar)); + element.appendChild(textnode); + element.setAttribute("TargetKeyChar", QString(m_targetKeyChar)); + if (m_fingerKeyChar != QChar(0)) + element.setAttribute("FingerKeyChar", QString(m_fingerKeyChar)); + if (!m_controlKeyId.isEmpty()) + element.setAttribute("ControlKeyId", m_controlKeyId); + root.appendChild(element); +*/ +} + diff --git a/ktouch/src/ktouchkeyconnector.h b/ktouch/src/ktouchkeyconnector.h new file mode 100644 index 00000000..a8d20dea --- /dev/null +++ b/ktouch/src/ktouchkeyconnector.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * ktouchkeyconnetor.h * + * ------------------- * + * Copyright (C) 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. * + ***************************************************************************/ + +#ifndef KTOUCHKEYCONNECTOR_H +#define KTOUCHKEYCONNECTOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qdom.h> +#include <qvaluevector.h> +#include "ktouchkey.h" + + /// The KTouchKeyConnector class contains the information about the character that has to + /// be pressed and the keys on the keyboard that should be highlighted. + /// It associates a character with a target key and optionally a finger and/or control key. +class KTouchKeyConnector { + public: + KTouchKeyConnector() {} + /// Constructor + KTouchKeyConnector(QChar keyChar, unsigned int target_key, unsigned int finger_key, + unsigned int modifier_key) + : m_keyChar(keyChar), m_targetKeyIndex(target_key), m_fingerKeyIndex(finger_key), + m_modifierKeyIndex(modifier_key) + {} + + /// Reads the key connector data from the DomElement + bool read(QDomNode node); + /// Creates a new DomElement, writes the key connector data into it and appends it to the root object. + void write(QDomDocument& doc, QDomElement& root) const; + + QChar m_keyChar; ///< This is the character that has to be pressed to access this key. + int m_targetKeyIndex; ///< Index of the target key (-1 if none). + int m_fingerKeyIndex; ///< Index of the finger key (-1 if none). + int m_modifierKeyIndex; ///< Index of the modifier key (-1 if none). +}; + +#endif // KTOUCHKEYCONNECTOR_H diff --git a/ktouch/src/ktouchkeys.cpp b/ktouch/src/ktouchkeys.cpp new file mode 100644 index 00000000..c254682b --- /dev/null +++ b/ktouch/src/ktouchkeys.cpp @@ -0,0 +1,223 @@ +/*************************************************************************** + * ktouchkeys.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 "ktouchkeys.h" +#include <kdebug.h> +#include <algorithm> // for std::min + +//#include "ktouch.h" +#include "prefs.h" +#include "ktouchcolorscheme.h" + +// Initialisation of static variables +int KTouchFingerKey::m_fingerKeyCount = 0; +// ----------------------------------------------------------------------------- + + +// ***** Implementation of class KTouchKey **** + +void KTouchBaseKey::paint(QPainter& p) const { + // We simply paint the key using the current brush and pen, so the derived classes + // will have to care about that + p.fillRect(m_xS, m_yS, m_wS, m_hS, p.brush()); + p.drawRect(m_xS, m_yS, m_wS, m_hS); +} + +void KTouchBaseKey::resize(double scale) { + m_xS = static_cast<int>(scale*m_x); + m_yS = static_cast<int>(scale*m_y); + m_wS = static_cast<int>(scale*m_w); + m_hS = static_cast<int>(scale*m_h); + // we set the font from the keyboard widget + m_font=Prefs::keyboardFont(); + m_font.setPointSize( static_cast<int>(std::min(m_wS,m_hS)/m_font_scale) ); +} +// ----------------------------------------------------------------------------- + + + +// **** Implementation of class KTouchNormalKey **** + +KTouchNormalKey::KTouchNormalKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h) + : KTouchBaseKey(keyChar, keyText, x, y, w, h), m_colorIndex(0) +{ + m_type = NORMAL_KEY; + m_font_scale =2; +} + +void KTouchNormalKey::paint(QPainter& p) const { + const KTouchColorScheme& colorScheme = KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()]; + QColor textColor; + if (m_isNextKey) { + // mark the key as "next" + p.setBrush( colorScheme.m_backgroundH ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_textH; + } + else { + p.setBrush( colorScheme.m_background[m_colorIndex] ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_text; + }; + KTouchBaseKey::paint(p); // call the parents paint() function + p.setPen(textColor); + + p.setFont( m_font ); + p.drawText(m_xS, m_yS, m_wS, m_hS, QPainter::AlignCenter, m_keyText); +} +// -------------------------------------------------------------- + + + +// **** Implementation of class KTouchFingerKey **** + +KTouchFingerKey::KTouchFingerKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h) + : KTouchNormalKey(keyChar, keyText, x, y, w, h) +{ + m_colorIndex = m_fingerKeyCount++; + if (m_colorIndex>=8) { + kdDebug() << "[KTouchFingerKey::KTouchFingerKey] Number of finger keys = " + << m_colorIndex << "! Setting colour index to 0" << endl; + m_colorIndex=0; + }; + m_type = FINGER_KEY; + m_font_scale =2; +} + +void KTouchFingerKey::paint(QPainter& p) const { + const KTouchColorScheme& colorScheme = KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()]; + p.setFont( m_font ); + if (m_isActive) { + p.setBrush( colorScheme.m_background[m_colorIndex] ); + p.setPen( colorScheme.m_frame ); + KTouchBaseKey::paint(p); // draw background and frame + p.setPen( QPen(colorScheme.m_frame,3) ); + p.drawRect(m_xS+2, m_yS+2, m_wS-4, m_hS-4); // mark it as "active" + p.setPen( colorScheme.m_text ); + p.drawText(m_xS, m_yS, m_wS, m_hS, QPainter::AlignCenter, m_keyText); + } + else if (m_isNextKey) { + p.setBrush( colorScheme.m_backgroundH ); + p.setPen( colorScheme.m_frame ); + KTouchBaseKey::paint(p); + p.setPen( colorScheme.m_textH ); + p.drawText(m_xS, m_yS, m_wS, m_hS, QPainter::AlignCenter, m_keyText); + } + else { + p.setBrush( colorScheme.m_background[m_colorIndex] ); + p.setPen( colorScheme.m_frame ); + KTouchBaseKey::paint(p); + p.drawRoundRect(m_xS+2, m_yS+2, m_wS-4, m_hS-4); + p.setPen( colorScheme.m_text ); + p.drawText(m_xS, m_yS, m_wS, m_hS, QPainter::AlignCenter, m_keyText); + }; +} +// -------------------------------------------------------------- + + + +// **** Implementation of class KTouchControlKey **** + +KTouchControlKey::KTouchControlKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h) + : KTouchBaseKey(keyChar, keyText, x, y, w, h) +{ + m_type = CONTROL_KEY; + m_font_scale = 4; +} + +void KTouchControlKey::paint(QPainter& p) const { + const KTouchColorScheme& colorScheme = KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()]; + p.setFont( m_font ); + QColor textColor; + if (m_isActive) { + p.setBrush( colorScheme.m_cBackgroundH ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_cTextH; + } + else if (m_isNextKey) { + p.setBrush( colorScheme.m_backgroundH ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_textH; + } + else { + p.setBrush( colorScheme.m_cBackground ); + p.setPen( colorScheme.m_frame ); + textColor = colorScheme.m_cText; + }; + KTouchBaseKey::paint(p); + p.setPen( textColor ); + int h = std::min(m_wS, m_hS); + int ch = static_cast<int>(h*0.5); // the height for the special chars + if (m_keyText=="Shift") { + int x = m_xS+h/2; + int y = m_yS+m_hS/2; + p.moveTo(x-ch/2, y); + p.lineTo(x-ch/4, y); + p.lineTo(x-ch/4, y+ch/2); + p.lineTo(x+ch/4, y+ch/2); + p.lineTo(x+ch/4, y); + p.lineTo(x+ch/2, y); + p.lineTo(x, y-ch/2); + p.lineTo(x-ch/2, y); + } + else if (m_keyText=="CapsLock") { + int x = m_xS+h/2; + int y = m_yS+m_hS/2; + p.moveTo(x-ch/2, y); + p.lineTo(x-ch/4, y); + p.lineTo(x-ch/4, y-ch/2); + p.lineTo(x+ch/4, y-ch/2); + p.lineTo(x+ch/4, y); + p.lineTo(x+ch/2, y); + p.lineTo(x, y+ch/2); + p.lineTo(x-ch/2, y); + } + else if (m_keyText=="Tab") { + int xleft = m_xS+h/2-ch/2; + int xright = m_xS + std::min(m_wS-h/2+ch/2,h); + int y = m_yS+m_hS/2; + p.drawLine(xleft, y,xleft, y-ch/2); + p.drawLine(xleft, y-ch/4, xright, y-ch/4); + p.drawLine(xleft, y-ch/4, xleft+ch/2, y-static_cast<int>(ch*0.10)); + p.drawLine(xleft, y-ch/4, xleft+ch/2, y-static_cast<int>(ch*0.40)); + p.drawLine(xright, y, xright, y+ch/2); + p.drawLine(xleft, y+ch/4, xright, y+ch/4); + p.drawLine(xright, y+ch/4, xright-ch/2, y+static_cast<int>(ch*0.10)); + p.drawLine(xright, y+ch/4, xright-ch/2, y+static_cast<int>(ch*0.40)); + + } + else if (m_keyText=="BackSpace") { + int xleft = m_xS+h/2-ch/2; + int xright = m_xS + std::min(m_wS-h/2+ch/2,h); + int y = m_yS+m_hS/2; + p.drawLine(xleft, y,xright, y); + p.drawLine(xleft, y, xleft+ch/2, y-static_cast<int>(ch*0.15)); + p.drawLine(xleft, y, xleft+ch/2, y+static_cast<int>(ch*0.15)); + } + else if (m_keyText=="Enter") { + int xleft = m_xS+h/2-ch/2; + int xright = m_xS + std::min(m_wS-h/2+ch/2,h); + int y = m_yS+m_hS/2; + p.drawLine(xright, y-ch/2,xright, y); + p.drawLine(xleft, y,xright, y); + p.drawLine(xleft, y, xleft+ch/3, y-static_cast<int>(ch*0.15)); + p.drawLine(xleft, y, xleft+ch/3, y+static_cast<int>(ch*0.15)); + } + else if (m_keyText=="AltGr") { + p.drawText(m_xS, m_yS, m_wS, m_hS, QPainter::AlignCenter | QPainter::AlignVCenter, "Alt Gr"); + } + else + p.drawText(m_xS, m_yS, m_wS, m_hS, QPainter::AlignCenter | QPainter::AlignVCenter, m_keyText); +} + + + diff --git a/ktouch/src/ktouchkeys.h b/ktouch/src/ktouchkeys.h new file mode 100644 index 00000000..90d4222d --- /dev/null +++ b/ktouch/src/ktouchkeys.h @@ -0,0 +1,151 @@ +/*************************************************************************** + * ktouchkeys.h * + * ------------ * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHKEYS_H +#define KTOUCHKEYS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qpainter.h> + +class KTouchFingerKey; +class KTouchNormalKey; +class KTouchControlKey; + +/** This is the base class for all keys (KTouchFingerKey, KTouchNormalKey and KTouchControlKey). + * Do not create instances of KTouchKey itself or you will get only blank keys. Note that the + * KTouchKey class hierarchy contains only the information for painting the keys. The connectivity + * is handled using the KTouchKeyConnector. + */ +class KTouchBaseKey { + public: + /// The possible types of the keys + enum KeyType { FINGER_KEY, NORMAL_KEY, CONTROL_KEY }; + + /// Constructor + KTouchBaseKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h) + : m_keyChar(keyChar), m_keyText(keyText), m_isActive(false), m_isNextKey(false), + m_x(x), m_y(y), m_w(w), m_h(h), m_type(NORMAL_KEY) {} + /// Destructor. + virtual ~KTouchBaseKey() {} + /// Paints the basic key shape using the painter p. + virtual void paint(QPainter& p) const; + /// Recalculates the scaled position and size properties of the key. + void resize(double scale); + /// Returns the boundary frame of the key. + QRect frame() const { return QRect(m_x, m_y, m_w, m_h); } + /// Returns the key type. + KeyType type() const { return m_type; } + + QChar m_keyChar; ///< The character that needs to be pressed to access this char. + QString m_keyText; ///< The text on the key (may be a single char only). + bool m_isActive; ///< Indicates whether the key is active (finger and control keys). + bool m_isNextKey; ///< Indicates whether this is the next to be pressed key (normal and finger keys). + QFont m_font; + float m_font_scale; + + protected: + int m_x; ///< The x position of the key. + int m_y; ///< The y position of the key. + int m_w; ///< The width of the key. + int m_h; ///< The height of the key. + + int m_xS; ///< The scaled x position of the key. + int m_yS; ///< The scaled y position of the key. + int m_wS; ///< The scaled width of the key. + int m_hS; ///< The scaled height of the key. + + KeyType m_type; ///< Stores the type of the key (convenience for saving of the keyboard layout). +}; +// --------------------------------------------------------------------------------------- + + + +/** This is a normal keyboard key with text. + * The background colour of the key will be taken from the current colour scheme using the + * colour index (this should be the same colour as used for the corresponding finger key). + */ +class KTouchNormalKey : public KTouchBaseKey { + public: + /// Constructor + KTouchNormalKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h); + /// Destructor + virtual ~KTouchNormalKey() {} + /// Extends the painting routine of KTouchKey (adds the text). + void paint(QPainter& p) const; + /// Indicates the colour index in the colour scheme, that has to be used for this key + /// and will be set in KTouchKeyboard::updateColors(). + unsigned int m_colorIndex; +}; +// ------------------------------------------------------------------------------------ + + + +/** This is a key where a finger rests while not typing. + * The name is taken from the fact, that there is normally for each finger (not including the + * thumbs) a key on a keyboard where the finger rests while it is not "used". A finger key + * is basically a normal key with additional stuff in the painting routine to mark it as a + * finger key. + */ +class KTouchFingerKey : public KTouchNormalKey { + public: + /// Constructor + KTouchFingerKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h); + /// Destructor + ~KTouchFingerKey() { --m_fingerKeyCount; } + /// Extends the painting algoritm of KTouchNormalKey when marked. + void paint(QPainter& p) const; + + private: + static int m_fingerKeyCount; ///< Contains the number of created finger keys (for colour generation). +}; +// ------------------------------------------------------------------------------------ + + + +/** This is a special or control key. + * This key acts as a modifier key to a normal key (for instance a shift key) and has a + * different shape and painting routine then the normal keys. Therefore it is directly + * derived from KTouchKey. + */ +class KTouchControlKey : public KTouchBaseKey { + public: + /// Constructor + KTouchControlKey(const QChar& keyChar, const QString& keyText, int x, int y, int w, int h); + /// Extends the parents paint routine (draws the text or other fancy stuff). + void paint(QPainter& p) const; +}; +// ------------------------------------------------------------------------------------ + + +/// This is the key connector which represents a key or key combination that can be pressed. + /// It associates a character with a target key and optionally a finger and/or control key. +class KTouchKeyConnection { + public: + /// Default constructor + KTouchKeyConnection() : m_keyChar(0), m_targetKeyChar(0), m_fingerKeyChar(0), m_controlKeyChar(0) {} + /// Constructor provided for convenience. + KTouchKeyConnection(const QChar& keyChar, const QChar& targetKeyChar, const QChar& fingerKeyChar, + const QChar& controlKeyChar) + : m_keyChar(keyChar), m_targetKeyChar(targetKeyChar), m_fingerKeyChar(fingerKeyChar), m_controlKeyChar(controlKeyChar) {} + + /// This is the character that has to be pressed to access this key. + QChar m_keyChar; + + QChar m_targetKeyChar; ///< The target character (the key the user has to press). + QChar m_fingerKeyChar; ///< The finger key (where the used finger normally rests). + QChar m_controlKeyChar; ///< The modifier key the user has to press also (for example LeftShift). +}; + +#endif // KTOUCHKEYS_H diff --git a/ktouch/src/ktouchlecture.cpp b/ktouch/src/ktouchlecture.cpp new file mode 100644 index 00000000..70195977 --- /dev/null +++ b/ktouch/src/ktouchlecture.cpp @@ -0,0 +1,300 @@ +/*************************************************************************** + * ktouchlecture.cpp * + * ----------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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 "ktouchlecture.h" + +#include <qfile.h> +#include <qtextstream.h> +#include <qstringlist.h> +#include <qdom.h> + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kio/netaccess.h> + +bool KTouchLecture::load(QWidget * window, const KURL& url) { + // Ok, first download the contents as usual using the KIO lib + // File is only downloaded if not local, otherwise it's just opened + QString target; + bool result = false; + if (KIO::NetAccess::download(url, target, window)) { + // Ok, that was successful, store the lectureURL and read the file + QFile infile(target); + if ( !infile.open( IO_ReadOnly ) ) { + KIO::NetAccess::removeTempFile(target); + return false; // Bugger it... couldn't open it... + } + QTextStream in( &infile ); + result = readLecture(in); + }; + KIO::NetAccess::removeTempFile(target); + return result; +} +// ---------------------------------------------------------------------------- + +bool KTouchLecture::loadXML(QWidget * window, const KURL& url) { + // Ok, first download the contents as usual using the KIO lib + // File is only downloaded if not local, otherwise it's just opened + QString target; + bool result = false; + if (KIO::NetAccess::download(url, target, window)) { + // Ok, that was successful, store the lectureURL and read the file + QFile infile(target); + if ( !infile.open( IO_ReadOnly ) ) { + KIO::NetAccess::removeTempFile(target); + return false; // Bugger it... couldn't open it... + } + QDomDocument doc; + doc.setContent( &infile ); + result = readLecture(doc); + } + KIO::NetAccess::removeTempFile(target); + return result; +} +// ---------------------------------------------------------------------------- + + +bool KTouchLecture::saveXML(QWidget * window, const KURL& url) const { + // create the XML document + QDomDocument doc; + writeLecture(doc); + + // and save it + QString tmpFile; + KTempFile *temp=0; + if (url.isLocalFile()) + tmpFile=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + temp->setAutoDelete(true); // so we don't have to delete the file ourselves + tmpFile=temp->name(); + } + + QFile outfile(tmpFile); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + // kdDebug() << "Error creating lecture file!" << endl; + return false; + }; + + QTextStream out( &outfile ); + out << doc.toString(); + outfile.close(); + // if we have a temporary file, we still need to upload it + if (temp) { + KIO::NetAccess::upload(tmpFile, url, window); + delete temp; + } + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchLecture::createDefault() { + m_lectureData.clear(); // remove everything else + m_title = i18n("A default lecture..."); + m_fontSuggestions = "Monospace"; + KTouchLevelData miniLevel; // create the level which is by default a mini-level + m_lectureData.push_back( miniLevel ); +} +// ---------------------------------------------------------------------------- + +const KTouchLevelData& KTouchLecture::level(unsigned int levelNum) const { + if (levelNum>=m_lectureData.size()) + levelNum=0; + return m_lectureData[levelNum]; +} +// ---------------------------------------------------------------------------- + +bool KTouchLecture::readLecture(QTextStream& in) { + //kdDebug() << "[KTouchLecture::loadLecture] Reading lecture file '" << lectureURL.url() << "'!" << endl; + QString line; + // remove everything else + m_lectureData.clear(); + // now loop until end of file is reached and break down the textfile into several strings containing the levels + QStringList slist; + QString current_level = QString::null; // used to store the current level data + line = in.readLine(); + bool in_level = false; + while (!in.atEnd() && !line.isNull()) { + // only consider non-empty lines + if (!line.isEmpty()) { + // lecture title? + if (line.find("# Title:") == 0) + m_title = line.right(line.length() - 8).stripWhiteSpace(); + else if (line[0]!='#' || line.find("# Comment:")!=-1) { + // ok, after all those comment lines, we finally found the beginn of a level + in_level = true; + current_level += line + '\n'; + } + else if (in_level) { + // ok, a new comment found, if we were reading a level, store it + slist.append(current_level); + current_level = QString::null; + in_level = false; + } + } + line = in.readLine(); + }; + if (!current_level.isEmpty() && in_level) + slist.append(current_level); + + //kdDebug() << "Levels read = " << slist.count() << endl; + + // now read all the levels + for (QStringList::Iterator it = slist.begin(); it!=slist.end(); ++it) { + // create new level + KTouchLevelData level; + QTextStream t(&(*it), IO_ReadOnly); + // try to read it + if (!level.readLevel(t)) { + // uh oh, error while reading level data + createDefault(); + return false; + }; + // add it (object will be deleted by the list) + m_lectureData.push_back(level); + } + + if (m_lectureData.size()>1) + return true; // all ok + else { + // Hmm, no levels in the file. So we create our default mini level and report an error. + createDefault(); + return false; + }; +} +// ---------------------------------------------------------------------------- + +bool KTouchLecture::readLecture(QDomDocument& doc) { + QString line; + m_lectureData.clear(); // clean current data + // retrieve the title + QDomNodeList entries = doc.elementsByTagName("Title"); + if (entries.count() >= 1) m_title = entries.item(0).firstChild().nodeValue(); + else m_title = i18n("untitled lecture"); + // retrieve the comment + entries = doc.elementsByTagName("Comment"); + if (entries.count() >= 1) + m_comment = entries.item(0).firstChild().nodeValue(); + // retrieve the font suggestion + entries = doc.elementsByTagName("FontSuggestions"); + if (entries.count() >= 1) + m_fontSuggestions = entries.item(0).firstChild().nodeValue(); + // retrieve the levels + entries = doc.elementsByTagName("Level"); + for (unsigned int i=0; i<entries.count(); ++i) { + KTouchLevelData level; + level.readLevel(entries.item(i)); + m_lectureData.push_back(level); + } + if (m_lectureData.size()>0) + return true; // all ok + else { + // Hmm, no levels in the file. So we create our default mini level and report an error. + createDefault(); + return false; + }; +} +// ---------------------------------------------------------------------------- + +void KTouchLecture::writeLecture(QDomDocument& doc) const { + QDomElement root = doc.createElement( "KTouchLecture" ); + doc.appendChild(root); + // Store title and ensure that the file contains a title! + QDomElement title = doc.createElement("Title"); + QDomText titleText; + if (m_title.isEmpty()) titleText = doc.createTextNode( i18n("untitled lecture") ); + else titleText = doc.createTextNode(m_title); + title.appendChild(titleText); + root.appendChild(title); + // Store comment if given + if (!m_comment.isEmpty()) { + QDomElement comment = doc.createElement("Comment"); + QDomText commentText = doc.createTextNode(m_comment); + comment.appendChild(commentText); + root.appendChild(comment); + } + // Store font suggestion if given + if (!m_fontSuggestions.isEmpty()) { + QDomElement font = doc.createElement("FontSuggestions"); + QDomText fontText = doc.createTextNode(m_fontSuggestions); + font.appendChild(fontText); + root.appendChild(font); + } + // Store levels + QDomElement levels = doc.createElement("Levels"); + root.appendChild(levels); + for (QValueVector<KTouchLevelData>::const_iterator it=m_lectureData.begin(); + it!=m_lectureData.end(); ++it) + { + it->writeLevel(doc, levels); + } +} +// ---------------------------------------------------------------------------- + + + +// OLD and deprecated stuff + +/* +bool KTouchLecture::save(QWidget * window, const KURL& url) const { + QString tmpFile; + KTempFile *temp=0; + if (url.isLocalFile()) + tmpFile=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + temp->setAutoDelete(true); // so we don't have to delete the file ourselves + tmpFile=temp->name(); + } + + QFile outfile(tmpFile); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + // kdDebug() << "Error creating lecture file!" << endl; + return false; + }; + QTextStream out( &outfile ); + writeLecture(out); + // TODO : check stream status to see if save worked + outfile.close(); + // if we have a temporary file, we still need to upload it + if (temp) { + KIO::NetAccess::upload(tmpFile, url, window); + delete temp; + } + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchLecture::writeLecture(QTextStream& out) const { + out << "###################################### " << endl; + out << "# # " << endl; + out << "# Training lecture file for KTouch # " << endl; + out << "# # " << endl; + out << "###################################### " << endl << endl; + out << "# Title: " << m_title << endl; + out << endl; + + int levelCounter=0; + for (QValueVector<KTouchLevelData>::const_iterator it=m_lectureData.begin(); it!=m_lectureData.end(); ++it) { + out << "################################" << endl; + out << "# Level: " << ++levelCounter << endl; + out << "# " << endl; + it->writeLevel(out); + }; + out << endl; +} +// ---------------------------------------------------------------------------- + +*/ diff --git a/ktouch/src/ktouchlecture.h b/ktouch/src/ktouchlecture.h new file mode 100644 index 00000000..3c59ee08 --- /dev/null +++ b/ktouch/src/ktouchlecture.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * ktouchlecture.h * + * --------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHLECTURE_H +#define KTOUCHLECTURE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +class QWidget; +class QDomDocument; +class KURL; + +#include <qvaluevector.h> +#include "ktouchleveldata.h" + +/// This class handles the lecture data and provides the lines to type. +/// +/// It contains the level data (see KTouchLevelData). A lecture object +/// contains <b>ALWAYS</b> at least one lecture.<p> +/// The lecture data can be read and written using the member functions +/// readLecture() and writeLecture().<p> +/// During a training session the program will occasionally need a new +/// line of text. You can retrieve the data of a certain level using +/// the member function level() and then the line in this level (see +/// KTouchLevelData). +class KTouchLecture { + public: + /// Default Constructor + KTouchLecture() { createDefault(); } + /// Creates a default mini-lecture. + void createDefault(); + /// Loads a lecture from file (returns true if successful). + bool load(QWidget * window, const KURL& url); + /// Loads a lecture (in XML format) from file (returns true if successful). + bool loadXML(QWidget * window, const KURL& url); + // /// Saves the lecture data to file (returns true if successful). + // bool save(QWidget * window, const KURL& url) const; + /// Saves the lecture data to file (returns true if successful). + bool saveXML(QWidget * window, const KURL& url) const; + /// Returns the number of levels in the lecture. + unsigned int levelCount() const { return m_lectureData.size(); } + /// Returns a reference to the data of the level. + /// If the level number is out of range the function will always return + /// the level 0. + const KTouchLevelData& level(unsigned int levelNum) const; + /// Returns the title of the lecture. + const QString& title() const { return m_title; } + /// Sets the title of the lecture. + void setTitle(const QString& title) { m_title = title; } + + QString m_title; ///< The title of the lecture. + QString m_comment; ///< A comment. + QString m_fontSuggestions; ///< Font suggestions for this lecture. + + private: + /// Loads a lecture from file + bool readLecture(QTextStream& in); + /// Loads a lecture from file into an XML document + bool readLecture(QDomDocument& doc); + /// Saves the lecture data to the current lecture URL + void writeLecture(QTextStream& out) const; + /// Saves the lecture data in the XML document + void writeLecture(QDomDocument& doc) const; + + QValueVector<KTouchLevelData> m_lectureData; ///< The lecture data. + + /// The editor should be able to handle the internal lecture data (it's for convenience actually). + friend class KTouchLectureEditor; +}; + +#endif // KTOUCHLECTURE_H diff --git a/ktouch/src/ktouchlectureeditor.cpp b/ktouch/src/ktouchlectureeditor.cpp new file mode 100644 index 00000000..ce433d8c --- /dev/null +++ b/ktouch/src/ktouchlectureeditor.cpp @@ -0,0 +1,420 @@ +/*************************************************************************** + * ktouchlectureeditor.cpp * + * ----------------------- * + * Copyright (C) 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 <qlabel.h> +#include <qstring.h> +#include <qstringlist.h> + +#include <kpushbutton.h> +#include <klistview.h> +#include <klineedit.h> +#include <ktextedit.h> +#include <klocale.h> +#include <kdebug.h> +#include <ksqueezedtextlabel.h> +#include <kmessagebox.h> +#include <kcombobox.h> +#include <kfontdialog.h> +#include <kfiledialog.h> + +#include <algorithm> // for std::swap + +#include "ktouchlectureeditor.h" +#include "ktouchlectureeditor.moc" + +#include "ktouchlecture.h" +#include "ktouchopenrequest.h" +#include "ktouch.h" + + +// ************************** +// ***** Public functions *** +// ************************** + +KTouchLectureEditor::KTouchLectureEditor(QWidget *parent, const char* name, bool modal, WFlags fl) + : KTouchLectureEditorDlg(parent, name, modal, fl) +{ + levelListView->setSorting(-1); // don't sort my level list view! + + connect(levelListView, SIGNAL(selectionChanged(QListViewItem*)),this, SLOT(newSelection(QListViewItem*)) ); + connect(newCharsEdit, SIGNAL(textChanged(const QString&)), this, SLOT(newCharsChanged(const QString&)) ); + connect(newBtn, SIGNAL(clicked()), this, SLOT(newLevel()) ); + connect(deleteBtn, SIGNAL(clicked()), this, SLOT(deleteLevel()) ); + connect(upBtn, SIGNAL(clicked()), this, SLOT(moveUp()) ); + connect(downBtn, SIGNAL(clicked()), this, SLOT(moveDown()) ); + + // make the connections for making the lecture modified + connect(titleEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setModified()) ); + connect(lectureCommentEdit, SIGNAL(textChanged()), this, SLOT(setModified()) ); + connect(levelCommentEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setModified()) ); + connect(linesTextEdit, SIGNAL(textChanged()), this, SLOT(setModified()) ); + + // The font, open, save, saveas and close buttons are already connected +} +// ----------------------------------------------------------------------------- + +bool KTouchLectureEditor::startEditor(const KURL& url) { + // call open request dialog and load a lecture, and start the dialogs event loop if + // the user did not cancel the open request dialog + if (openLectureFile(url)==QDialog::Accepted) { + exec(); + return true; + } + else return false; +} +// ----------------------------------------------------------------------------- + + +// ************************* +// ***** Private slots ***** +// ************************* + +void KTouchLectureEditor::fontBtnClicked() { + QFont f(m_lecture.m_fontSuggestions); + if (KFontDialog::getFont(f, false, this, true)==QDialog::Accepted) { + linesTextEdit->setFont(f); + lectureCommentEdit->setFont(f); + levelCommentEdit->setFont(f); + newCharsEdit->setFont(f); + titleEdit->setFont(f); + levelListView->setFont(f); + m_lecture.m_fontSuggestions = f.family(); + setModified(); + } +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::openBtnClicked() { + saveModified(); // save if modified + openLectureFile(""); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::saveBtnClicked() { + if (m_currentURL.isEmpty()) saveAsBtnClicked(); + else { + transfer_from_dialog(); + m_lecture.saveXML(this, m_currentURL); + setModified(false); + } +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::saveAsBtnClicked() { + QString tmp = KFileDialog::getSaveFileName(QString::null, + "*.ktouch.xml|KTouch Lecture Files(*.ktouch.xml)\n*.*|All Files", this, i18n("Save Training Lecture") ); + if (!tmp.isEmpty()) { + transfer_from_dialog(); + m_currentURL = tmp; + m_lecture.saveXML(this, m_currentURL); + setCaption(m_currentURL.url()); + setModified(false); + } +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::newSelection(QListViewItem* item) { + if (m_selecting) return; + bool current_modified_flag = m_modified; // remember our current status + // first store the current level data + storeCurrentLevel(); + // now we need to find the level which has been selected + QListViewItem *i = levelListView->firstChild(); + unsigned int level=0; + while (i!=0 && i!=item) { + i = i->nextSibling(); + ++level; + }; + if (i!=0) { + m_currentItem = i; + m_level = level; + showCurrentLevel(); + } + m_selecting = true; // prevent the selectionChanged() signal from interfering + levelListView->setSelected(m_currentItem, true); + m_selecting = false; + + if (m_lecture.m_lectureData.size()==1) { + downBtn->setEnabled(false); + upBtn->setEnabled(false); + } + else { + if (m_level==m_lecture.m_lectureData.size()-1) downBtn->setEnabled(false); + else downBtn->setEnabled(true); + if (m_level==0) upBtn->setEnabled(false); + else upBtn->setEnabled(true); + }; + setModified(current_modified_flag); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::newCharsChanged(const QString& text) { + if (m_currentItem==0) return; // shouldn't happen, but a little bit of paranoia... + m_currentItem->setText(0, text); + setModified(); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::newLevel() { + createNewLevel(); + QListViewItem *newItem = new QListViewItem( levelListView, + levelListView->lastItem(), m_lecture.m_lectureData.back().m_newChars ); + newSelection(newItem); + upBtn->setEnabled(true); + downBtn->setEnabled(false); + deleteBtn->setEnabled(true); + setModified(); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::deleteLevel() { + // Open for discussion: Should there be a message box to confirm the delete? + if (m_level >= m_lecture.m_lectureData.size()) return; // paranoia check + m_selecting = true; // prevent the selectionChanged() signal from interfering + // first remove the item from the list view + delete m_currentItem; + // then remove the level data + QValueVector<KTouchLevelData>::iterator it=m_lecture.m_lectureData.begin(); + std::advance(it, m_level); + m_lecture.m_lectureData.erase(it); + m_currentItem = levelListView->firstChild(); + for (unsigned int i=0; i<m_level; ++i) + m_currentItem = m_currentItem->nextSibling(); + levelListView->setSelected(m_currentItem, true); + showCurrentLevel(); + m_selecting = false; + if (m_lecture.m_lectureData.size()==1) + deleteBtn->setEnabled(false); + if (m_level==m_lecture.m_lectureData.size()-1) + downBtn->setEnabled(false); + if (m_level==0) + upBtn->setEnabled(false); + setModified(); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::moveUp() { + if (m_level==0) return; + m_selecting=true; // again, I don't want to process changeSelection() signals now + storeCurrentLevel(); + QListViewItem *upperItem = m_currentItem->itemAbove(); + std::swap(m_lecture.m_lectureData[m_level], m_lecture.m_lectureData[m_level-1]); + upperItem->setText(0, m_lecture.m_lectureData[m_level-1].m_newChars); + m_currentItem->setText(0, m_lecture.m_lectureData[m_level].m_newChars); + m_currentItem=upperItem; + --m_level; + levelListView->setSelected(m_currentItem, true); + showCurrentLevel(); + m_selecting = false; + if (m_level==0) + upBtn->setEnabled(false); + downBtn->setEnabled(true); + setModified(); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::moveDown() { + if (m_level>=m_lecture.m_lectureData.size()-1) return; + m_selecting=true; // again, I don't want to process changeSelection() signals now + storeCurrentLevel(); + QListViewItem *lowerItem = m_currentItem->itemBelow(); + std::swap(m_lecture.m_lectureData[m_level], m_lecture.m_lectureData[m_level+1]); + m_currentItem->setText(0, m_lecture.m_lectureData[m_level].m_newChars); + lowerItem->setText(0, m_lecture.m_lectureData[m_level+1].m_newChars); + m_currentItem=lowerItem; + ++m_level; + levelListView->setSelected(m_currentItem, true); + showCurrentLevel(); + m_selecting = false; + if (m_level==m_lecture.m_lectureData.size()-1) + downBtn->setEnabled(false); + upBtn->setEnabled(true); + setModified(); +} +// ----------------------------------------------------------------------------- + + + +// **************************** +// ***** Private functions **** +// **************************** + +void KTouchLectureEditor::transfer_to_dialog() { + bool current_modified_flag = m_modified; + // set the title and the filename of the lecture + titleEdit->setText(m_lecture.title()); + lectureCommentEdit->setText(m_lecture.m_comment); + if (m_currentURL.isEmpty()) setCaption(i18n("KTouch Lecture Editor - ") + i18n("<new unnamed lecture file>")); + else setCaption(i18n("KTouch Lecture Editor - ") + m_currentURL.fileName()); + // copy the 'new char' strings of the lectures into the list view + levelListView->clear(); + QValueVector<KTouchLevelData>::const_iterator it=m_lecture.m_lectureData.begin(); + // add first item + QListViewItem *item=new QListViewItem( levelListView, (it++)->m_newChars ); + // add all the others + for (;it!=m_lecture.m_lectureData.end(); ++it) + item = new QListViewItem( levelListView, item, it->m_newChars ); + m_currentItem=levelListView->firstChild(); + m_selecting = true; // prevent the selectionChanged() signal from interfering + levelListView->setSelected(m_currentItem, true); + m_selecting = false; + // open first lesson + m_level=0; + showCurrentLevel(); + // set up the buttons + upBtn->setEnabled(false); + if (m_lecture.m_lectureData.size()>1) { + downBtn->setEnabled(true); + deleteBtn->setEnabled(true); + } + else { + downBtn->setEnabled(false); + deleteBtn->setEnabled(false); + } + // finally the font + if (!m_lecture.m_fontSuggestions.isEmpty()) { + QFont f("Monospace"); + // TODO : multiple font suggestions + f.fromString(m_lecture.m_fontSuggestions); + linesTextEdit->setFont(f); + lectureCommentEdit->setFont(f); + levelCommentEdit->setFont(f); + newCharsEdit->setFont(f); + titleEdit->setFont(f); + levelListView->setFont(f); + } + setModified(current_modified_flag); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::transfer_from_dialog() { + storeCurrentLevel(); + m_lecture.m_title = titleEdit->text(); + m_lecture.m_comment = lectureCommentEdit->text(); + setModified(); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::showCurrentLevel() { + if (m_level >= m_lecture.m_lectureData.size()) return; // should not happen, but why running a risk here... + levelLabel->setText(i18n("Data of Level %1").arg(m_level+1) ); + levelCommentEdit->setText(m_lecture.m_lectureData[m_level].m_comment); + newCharsEdit->setText(m_lecture.m_lectureData[m_level].m_newChars); + QString text; + for (QValueVector<QString>::const_iterator it=m_lecture.m_lectureData[m_level].m_lines.begin(); + it!=m_lecture.m_lectureData[m_level].m_lines.end(); ++it) + { + text += *it + '\n'; + } + linesTextEdit->setText(text); +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::storeCurrentLevel() { + if (m_level>=m_lecture.m_lectureData.size()) return; // should not happen, but ... better check one time too much... + m_lecture.m_lectureData[m_level].m_comment = levelCommentEdit->text(); + m_lecture.m_lectureData[m_level].m_newChars = newCharsEdit->text(); + m_lecture.m_lectureData[m_level].m_lines.clear(); + QString text = linesTextEdit->text(); + QStringList lines; + QString currentLine; + for (unsigned int i=0; i<text.length(); ++i) { + QChar c = text[i]; + if (c=='\t') c=' '; // replace tabs with spaces + if (c=='\n') { + lines.append(currentLine); + currentLine = ""; + } + else + currentLine += c; + }; + lines.append(currentLine); + for (QStringList::const_iterator it=lines.begin(); it!=lines.end(); ++it) { + if ((*it).isEmpty()) continue; + m_lecture.m_lectureData[m_level].m_lines.push_back(*it); + } +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::createNewLevel() { + KTouchLevelData newLevel; + newLevel.m_newChars = i18n("abcdefghijklmnopqrstuvwxyz"); + newLevel.m_comment = QString(); + newLevel.m_lines.clear(); // remove the lines of the default mini level + newLevel.m_lines.push_back(i18n("Enter your lines here...")); + m_lecture.m_lectureData.push_back(newLevel); +} +// ----------------------------------------------------------------------------- + +int KTouchLectureEditor::openLectureFile(const KURL& url) { + // First setup the open request dialog + KTouchOpenRequest dlg(this); + // Call the dialog + KURL new_url; + int result = dlg.requestFileToOpen(new_url, + i18n("Open Lecture File"), + i18n("Which Lecture File Would You Like to Edit?"), + i18n("Edit current lecture:"), + i18n("Open a default lecture:"), + i18n("Open a lecture file:"), + i18n("Create new lecture"), + url, KTouchPtr->lectureFiles(), i18n("<no lecture files available>")); + + if (result == QDialog::Accepted) { + // Ok, user confirmed the dialog, now lets get the url + m_currentURL = new_url; + // Try to load the lecture, if that failes, we create a new lecture instead + m_lecture = KTouchLecture(); // empty the lecture + if (!m_currentURL.isEmpty()) { + // try to read old format first then XML format + if (!m_lecture.load(this, m_currentURL) && !m_lecture.loadXML(this, m_currentURL)) { + KMessageBox::sorry(this, i18n("Could not open the lecture file, creating a new one instead.")); + m_currentURL = QString::null; // new lectures haven't got a URL + } + } + // If we have no URL, we create a new lecture - can happen if either the user + // chose "new lecture" or the chosen lecture could not be opened + if (m_currentURL.isEmpty()) { + m_lecture.createDefault(); + m_modified = true; // new lectures are modified by default + } + else + m_modified = false; // newly read lectures are not modified in the begin + // Update our editor with the lecture data + transfer_to_dialog(); + return QDialog::Accepted; + } + else return QDialog::Rejected; +} +// ----------------------------------------------------------------------------- + +void KTouchLectureEditor::setModified(bool flag) { + m_modified = flag; + if (!m_currentURL.isEmpty()) { + if (flag) setCaption(i18n("KTouch Lecture Editor - ") + m_currentURL.fileName() + i18n(" (modified)")); + else setCaption(i18n("KTouch Lecture Editor - ") + m_currentURL.fileName()); + } +} +// ----------------------------------------------------------------------------- + +bool KTouchLectureEditor::saveModified() { + if (!m_modified) return true; + // ok, ask the user to save the changes + int result = KMessageBox::questionYesNoCancel(this, + i18n("The lecture has been changed. Do you want to save the changes?"),QString::null,KStdGuiItem::save(),KStdGuiItem::discard()); + if (result == KMessageBox::Cancel) return false; // User aborted + if (result == KMessageBox::Yes) saveBtnClicked(); + // if successfully saved the modified flag will be resetted in the saveBtnClicked() function + return true; // User acknowledged +} +// ----------------------------------------------------------------------------- diff --git a/ktouch/src/ktouchlectureeditor.h b/ktouch/src/ktouchlectureeditor.h new file mode 100644 index 00000000..49850b6b --- /dev/null +++ b/ktouch/src/ktouchlectureeditor.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * ktouchlectureeditor.h * + * --------------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHLECTUREEDITOR_H +#define KTOUCHLECTUREEDITOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qwidget.h> +#include <qfont.h> +#include <kurl.h> + +#include "ktouchlectureeditor_dlg.h" +#include "ktouchlecture.h" + +/// This is the KTouch lecture editor dialog. +/// +/// The lecture edit dialog works like a standalone edit tool for editing lectures. +/// Upon opening of the dialog the 'open lecture' dialog box is shown where +/// the user can either select a local training lecture or load a lecture file. +/// Then the user can edit the lecture and save it (if desired under a different +/// file name). +/// The use of the editor is simple, just include ktouchlectureeditor.h and do +/// @code +/// KTouchLectureEditor dlg; +/// bool ok = dlg.startEditor(current_lecture); +/// // current_lecture should be the URL pointing to the current/default +/// // lecture or an empty URL +/// @endcode +/// @see startEditor() for details on the return value. +class KTouchLectureEditor : public KTouchLectureEditorDlg { + Q_OBJECT + public: + /// Constructor + KTouchLectureEditor(QWidget *parent, const char* name = 0, bool modal = FALSE, WFlags fl = 0); + /// Destructor + ~KTouchLectureEditor() {} + /// Shows and executes the dialog (argument is the url to the default or + /// current lecture file). + /// @return Returns 'true' if the dialog was properly executed (thus reloading + /// of the lecture in KTouch is necessary) or 'false' if user canceled + /// the "Open request" dialog. + bool startEditor(const KURL& url); + + private slots: + /// Called when the Font button was clicked. + void fontBtnClicked(); + /// Called when the Open button was clicked. + void openBtnClicked(); + /// Called when the Save button was clicked. + void saveBtnClicked(); + /// Called when the Save As button was clicked. + void saveAsBtnClicked(); + /// Called when the editor is closed. + void closeQuery() { if (saveModified()) accept(); } + /// Called when the editor is rejected (x clicked). + void reject() { closeQuery(); } + /// Will be called whenever some changes are made. + void setModified() { setModified(true); } + /// Will be called whenever another level has been selected. + void newSelection(QListViewItem* item); + /// Will be called whenever the new chars description is changed. + void newCharsChanged(const QString& text); + /// Will be called when the "New Level" button has been clicked. + void newLevel(); + /// Will be called when the "Delete Level" button has been clicked. + void deleteLevel(); + /// Will be called when the "Move Level Up" button has been clicked. + void moveUp(); + /// Will be called when the "Move Level Down" button has been clicked. + void moveDown(); + + private: + /// Transfers data from the lecture object to the dialog. + void transfer_to_dialog(); + /// Transfers data from the dialog to the lecture object. + void transfer_from_dialog(); + /// Fills the level user interface with data from the current level (stored in m_level). + void showCurrentLevel(); + /// Copies the content of the user interface into the vector with the level data. + void storeCurrentLevel(); + /// Creates a default level and appends it to the already existing levels. + void createNewLevel(); + /// Setups and opens the 'Open request dialog'. The url 'url' is used in the + /// open request dialog for the 'current' url selection. When the open request + /// dialog is closed the function attempts to load the lecture or if that failes + /// creates a new lecture. + /// @return The function returns the return code from the open request dialog, so + /// that one can react on a "cancel" choice. + int openLectureFile(const KURL& url); + /// Changes the state of the lecture file (flag==true means modified). + void setModified(bool flag); + /// If the lecture is modified the user is prompted to save it or throw away the changes. + /// If the user accepts the dialog the file is saved. + /// @return The function returns 'false' if the user aborted the save request, otherwise true. + bool saveModified(); + + KTouchLecture m_lecture; ///< The currently used lecture. + unsigned int m_level; ///< The current level number. + QListViewItem *m_currentItem; ///< The currently selected item in the list view. + bool m_selecting; ///< Flag to prevent the selection slot from selecting twice. + bool m_modified; ///< Flag indicating whether the lecture has been modified. + KURL m_currentURL; ///< URL of the current lecture. +}; + +#endif diff --git a/ktouch/src/ktouchlectureeditor_dlg.ui b/ktouch/src/ktouchlectureeditor_dlg.ui new file mode 100644 index 00000000..a5008e97 --- /dev/null +++ b/ktouch/src/ktouchlectureeditor_dlg.ui @@ -0,0 +1,684 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchLectureEditorDlg</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KTouchLectureEditorDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>722</width> + <height>557</height> + </rect> + </property> + <property name="caption"> + <string>KTouch Lecture Editor</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Lecture Properties</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>titleLabel</cstring> + </property> + <property name="font"> + <font> + <family>Helvetica</family> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Title:</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>titleEdit</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Comment:</string> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>lectureCommentEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>70</height> + </size> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Level Editor</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Levels</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>levelListView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>upBtn</cstring> + </property> + <property name="minimumSize"> + <size> + <width>35</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>downBtn</cstring> + </property> + <property name="minimumSize"> + <size> + <width>35</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image1</pixmap> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>newBtn</cstring> + </property> + <property name="minimumSize"> + <size> + <width>35</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image2</pixmap> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>deleteBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>35</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image3</pixmap> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit" row="2" column="1"> + <property name="name"> + <cstring>newCharsEdit</cstring> + </property> + </widget> + <widget class="QFrame" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>commentLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Level comment (optional):</string> + </property> + </widget> + <widget class="QLayoutWidget" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>wordCountLabel</cstring> + </property> + <property name="text"> + <string>Words: </string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>charCountLabel</cstring> + </property> + <property name="text"> + <string>Characters: </string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Time needed (beg/adv/pro): </string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>levelLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <family>Helvetica</family> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Level Data of Level 10</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>New characters in this level:</string> + </property> + </widget> + <widget class="KTextEdit" row="6" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>linesTextEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <family>Monospace</family> + </font> + </property> + <property name="wordWrap"> + <enum>NoWrap</enum> + </property> + </widget> + <widget class="QLabel" row="5" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Level data:</string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="1"> + <property name="name"> + <cstring>levelCommentEdit</cstring> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout35</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>fontButton</cstring> + </property> + <property name="text"> + <string>Change Font...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>248</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>openButton</cstring> + </property> + <property name="text"> + <string>&Open...</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>saveButton</cstring> + </property> + <property name="text"> + <string>&Save</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>saveAsButton</cstring> + </property> + <property name="text"> + <string>Save &As...</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>quitButton</cstring> + </property> + <property name="text"> + <string>&Close</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="577">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff610000020849444154789c6d933168db401885bf331aac9280ce68b087423234e0d225940eb5b7aeddba762bdd3276ee544a296d860e1dbb766b3b9466092493e44ddeaca2984a10810a16778198c810c37538db91dd080ef1dfbdefddbb3b7ea1120525e0435cc674fd2ef29e34d43e3dd0a2be5ed737e232be152e2e6154c0afb375f34dbd5089fa0f1e4dc075a03d8393290c3278b5a3c566524a6c824d583ae035c1ed40670bf0e0ed6fcc6d49844ad4cd99351440c701b66b77700edf7308cfe0e3e3f5248d259c6a281c98cd2d5c5d5bb8ba067917da1e3477e1e58f782d8903c025341de84c818eadddedf5ffd32e5c0ca178d4a3b5278c4a94a08406403a87b4b470a12bd8865151f12d837462eb42573cdf07b709cfbe185a7b2d83bf30381943af0d4ca0235d3887a3cce5f067c449e642b1989fc08b0730b3b989cbd81ec1db8230d7f4762451aa39cc25f171c8f04d1fbc2ba2b9cbc15c73bf2d198c35d54c02d0f5bb36c1d118763d4995c15126191e471606060777180ee134975439747dc9c57cf13c253641aa21cc345fa792e838227bf770f584c61884105c7cba62b4eff2644bc3dc26c05f18fc9dc2e752929e46641f6ee0200c84f5b026d57b83dc97e8bc5add01c618a31245fbf51f03ac461006a844118401c61813840180a9eb54a26e7aa1dfeb9bface9b5db7ac5bbdd64aa7422556bd1084815089124b78b3eb96755d870fff0082a93b063a293c500000000049454e44ae426082</data> + </image> + <image name="image1"> + <data format="PNG" length="578">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff610000020949444154789c6d93bf6b535114c73faf54788106f24a0a2928b46085e71670493b555c74b45bfd0fba8a9338155deaa2b483601749478b5307c10c91a4102182a53c798546ac18a98ffbc4421244f83adcbc9797dabb1ceeb9dfeff79c7b7e389244e6c447b1430414218802fca20f117815ef3f5c10054c248e5ac75a6fc153961c44c11879e7c0da545c92de1d4b87a7d2cbb604a8d16c604283691a9439df438b5baf599c691a9bc1fc14cc4e41e5323c7c2b961697743e72ad036e093c170685a1b3089300ed085666c09b849b5761b02796169db43cf109944be0e52c6ff06df40d9b4101ba67c0252817a132076bbbbd516d0ae00e83767e427f98815ff4adc0fe1798cd0367e0e561a5048b7339b63f58710fc8e5ecfbfc0cfc3a19aa455881b800af0f803c74e33e78502ef5f18be0feed8ffc79d8da87eee76e5a0324a9fa495aaf4bd5f6b0dca717dbcda6b4b66d3b90742b6dd3dd9d9e96b7a56aab27fd910ebf9a31bb51efa9f258724b7e2a604203261cf5bab2252def49d596a49ea4636b37ebd2ea9654b8b13a46364d03c9d02422feb39eca2fa46acb48bfa58dba91ff482adcba3f464e788e090dc9ec7b0b7670ae3f07f70adc71a13380daaba7fc78f30000139ab15d9908a220bdc447b103d0b81d3bddf76d763ec2ee937b63e42cde2ffa3683ecd6651fa7af4da7a37c3e72829fc86eddf92d34a1711acd867351e4e4fe0f117aaa22d2d74c9d0000000049454e44ae426082</data> + </image> + <image name="image2"> + <data format="PNG" length="611">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff610000022a49444154789c8d93b16bd47014c73f2957f0a4435352c881cb814b6ebba3d315070717fd0f1c0517ff8576ed50c4b15d6ab7ea22d4491d4a55903b07bd14844be42a0994e312da90940ecd0d85af4372d74aad383c783ff87ddffbf0def719e9208504b0c04f7ceaf36db931b46c70638f96dde0eafb341cf3d0a91b93ff46a7dbc1b11cfcc4c7b11cbac3508feeb7c873a856213bc93117ab7887110dbbc6bbef2ea7dfa07e6f6c389683910ed2a99804c6f3a66a7781592007aac0093007c4108dc14f3ca2c382646622f6131f2c70631766212fc55996c322847106166cbcdde6e3d7317e7cc0fb2fa18cbfcde01a4156120c8179887ec2f856cef687d7cc4c3a4f486a9f0cd832c8f3fc92c02c096ce8fe0a19cf819bf8506932e3580ee6de82daf1b248801a0054ab55d834303fdf860ceab6093138569df0d4a37da705173e680349b6d445d35c7691074cf3be37d2f9b1d47103a547d27eb7af9df59e48076959e47919a332f6253d9676910249a9d4f3241d4be7ae747e24adacbd149d6ee7b24880a49e244d8982a3543a93fa8391944a7d6f241d4bbd1f81566e26d82fe349417024e94cd2e04f9295b51dfdf70c8260249d49bd2b043bebbdc207e6de82a841d7ee18ed785944c033c1a6516c654960813784865538120b56b7b60b1f640f52a36b770cc772202ad61845193c15e1520a16847144c3022f8e4ac7865069de700b36605214bbd29121a5185a16ac6ebdba7e8d2fdeec8a4a132e0e702a4dfc8b03fef5fe0d7f23e504186855b00000000049454e44ae426082</data> + </image> + <image name="image3"> + <data format="PNG" length="524">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff61000001d349444154789c7d93318b134118869f098bec86dc712ba3ec420a07b498c8155944305dea7476fe0e7f83a53f21ad9676dac5424861b1e97620ca45442e0b37ec0672b08704d66236b98460a619bedd79bf79be074614f3022cdc75c23ab529894c482d2412f6ebd58f29ea12a1a5060b4830d6d032d6407378344cb8780aa321f851c96898507a4b462fe0e299cfe7afd4dbf3c61ab4d488625e60ac417506757c895b151000374007b21cc25b6005137348d2da764a6deab24db82c2b78048b7c494fc2fb4f6326dfa6cc72734022f61d1c11948e803f6e9ffccc509e62fce5036fdfbc16470e0e08d61584b0cc4b8860fa2b437514a935684fef1cb4b4744522131082a02da082f02c8012e22884b660f0f239be0d187413661b173e7650d76e84b6a0bc71043c10004cbf5fe13f01739be13704481c01164750b16b123e6e837061b29a7e576156f0aadb8386e0d84100d5baba2701b2f9354460560bfa1118bb38e1a082e02cb8bf19e845319985442ab0a02375c241809b7d7f9d0b7a11647649d010fcdfc1ba991de06f0dbfebbd26b17b23913ae1e05cecc28bbc04098bab6bf7eda1700f2c3fe5a0a8ddcd165414ba5dc664f3dafdb3a0e5a1036fdbe9ddc7317d4f33db187c4f73b73127eb6dee1f6c922615d31032450000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>openButton</sender> + <signal>clicked()</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>openBtnClicked()</slot> + </connection> + <connection> + <sender>saveButton</sender> + <signal>clicked()</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>saveBtnClicked()</slot> + </connection> + <connection> + <sender>saveAsButton</sender> + <signal>clicked()</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>saveAsBtnClicked()</slot> + </connection> + <connection> + <sender>quitButton</sender> + <signal>clicked()</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>closeQuery()</slot> + </connection> + <connection> + <sender>titleEdit</sender> + <signal>textChanged(const QString&)</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>setModified()</slot> + </connection> + <connection> + <sender>levelCommentEdit</sender> + <signal>textChanged(const QString&)</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>setModified()</slot> + </connection> + <connection> + <sender>newCharsEdit</sender> + <signal>textChanged(const QString&)</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>setModified()</slot> + </connection> + <connection> + <sender>linesTextEdit</sender> + <signal>textChanged()</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>setModified()</slot> + </connection> + <connection> + <sender>fontButton</sender> + <signal>clicked()</signal> + <receiver>KTouchLectureEditorDlg</receiver> + <slot>fontBtnClicked()</slot> + </connection> +</connections> +<tabstops> + <tabstop>titleEdit</tabstop> + <tabstop>levelListView</tabstop> + <tabstop>upBtn</tabstop> + <tabstop>downBtn</tabstop> + <tabstop>newBtn</tabstop> + <tabstop>deleteBtn</tabstop> + <tabstop>levelCommentEdit</tabstop> + <tabstop>newCharsEdit</tabstop> + <tabstop>linesTextEdit</tabstop> + <tabstop>fontButton</tabstop> + <tabstop>openButton</tabstop> + <tabstop>saveButton</tabstop> + <tabstop>saveAsButton</tabstop> + <tabstop>quitButton</tabstop> +</tabstops> +<slots> + <slot>openBtnClicked()</slot> + <slot>saveBtnClicked()</slot> + <slot>saveAsBtnClicked()</slot> + <slot>closeQuery()</slot> + <slot>setModified()</slot> + <slot>fontBtnClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klistview.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>ktextedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchleveldata.cpp b/ktouch/src/ktouchleveldata.cpp new file mode 100644 index 00000000..c74c5d10 --- /dev/null +++ b/ktouch/src/ktouchleveldata.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + * ktouchleveldata.cpp * + * ----------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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 "ktouchleveldata.h" +#include <qiodevice.h> +#include <qdom.h> +#include <klocale.h> +#include <kdebug.h> +#include <qstringlist.h> + +const QString& KTouchLevelData::line(unsigned int lineNumber) const { + if (lineNumber>=m_lines.size()) + lineNumber=0; + return m_lines[lineNumber]; +} + +void KTouchLevelData::createDefault() { + m_newChars = i18n("Quite a lot"); + m_lines.clear(); + QString text = i18n("This is a small default text. If you want\n" + "to start practicing touch typing, open\n" + "a lecture/training file from the main menu.\n" + "If you press enter after the next line you have\n" + "successfully completed the default lecture. Hurray!!!"); + QStringList l( QStringList::split(QChar('\n'), text) ); + for (QStringList::const_iterator it = l.constBegin(); it != l.constEnd(); ++it) + m_lines.push_back( *it ); +} + +bool KTouchLevelData::readLevel(QTextStream& in) { + QString line; + m_comment = m_newChars = QString::null; + m_lines.clear(); + // now read all the lines of the level, until EOF or an empty line + line = in.readLine(); + while (!line.isNull()) { + if (!line.isEmpty()) { + // Do we have a comment here? And if yes, is a keyword in the line? + if (line.find("# Comment:")==0) + m_comment = line.right(line.length() - 10).stripWhiteSpace(); + else if (line[0]!='#') { + // the first line is the new characters line + if (m_newChars.isNull()) m_newChars = line; + else m_lines.push_back(line); + } + } + line = in.readLine(); + }; + if (m_lines.empty()) { + createDefault(); + return false; + } + return true; +} + +bool KTouchLevelData::readLevel(QDomNode in) { + QDomNode newChars = in.namedItem("NewCharacters"); + if (newChars.isNull()) m_newChars = i18n("basically all characters on the keyboard","abcdefghijklmnopqrstuvwxyz"); + else m_newChars = newChars.firstChild().nodeValue(); + QDomNode levelComment = in.namedItem("LevelComment"); + if (!levelComment.isNull()) m_comment = levelComment.firstChild().nodeValue(); + m_lines.clear(); + QDomNode line = in.namedItem("Line"); + while ( !line.isNull() ) { + QString l = line.firstChild().nodeValue(); + m_lines.push_back( l.stripWhiteSpace() ); + line = line.nextSibling(); + } + if (m_lines.empty()) { + createDefault(); + return false; + } + return true; +} + +void KTouchLevelData::writeLevel(QTextStream& out) const { + out << "# Comment: " << m_comment << endl; // comment line + out << m_newChars << endl; // new characters line + for (QValueVector<QString>::const_iterator it=m_lines.begin(); it!=m_lines.end(); ++it) + out << *it << endl; + out << endl; +} + +void KTouchLevelData::writeLevel(QDomDocument& doc, QDomElement& root) const { + QDomElement level = doc.createElement("Level"); + root.appendChild(level); + // comment (optional) + if (!m_comment.isEmpty()) { + QDomElement comment = doc.createElement("LevelComment"); + QDomText commentText = doc.createTextNode(m_comment); + comment.appendChild(commentText); + level.appendChild(comment); + } + // new characters (must have that) + QDomElement newchars = doc.createElement("NewCharacters"); + QDomText newcharsText; + if (m_newChars.isEmpty()) + newcharsText = doc.createTextNode(i18n("basically all characters on the keyboard","abcdefghijklmnopqrstuvwxyz")); + else + newcharsText = doc.createTextNode(m_newChars); + newchars.appendChild(newcharsText); + level.appendChild(newchars); + // the lines + for (QValueVector<QString>::const_iterator it=m_lines.begin(); it!=m_lines.end(); ++it) { + QDomElement line = doc.createElement("Line"); + QDomText lineText = doc.createTextNode(*it); + line.appendChild(lineText); + level.appendChild(line); + } +} + diff --git a/ktouch/src/ktouchleveldata.h b/ktouch/src/ktouchleveldata.h new file mode 100644 index 00000000..8055824d --- /dev/null +++ b/ktouch/src/ktouchleveldata.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * ktouchleveldata.h * + * ----------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHLEVELDATA_H +#define KTOUCHLEVELDATA_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qstring.h> +#include <qtextstream.h> +#include <qvaluevector.h> +#include <qdom.h> + +class QDomDocument; +class QDomElement; + +/// This class contains the data of one level in a lecture and provides +/// access to the lines of that level. +/// +/// A lecture (KTouchLecture) typically contains multiple levels. Each +/// level contains a level description, a string with the +/// newly introduced characters and several lines of training text. +/// There is ALWAYS at least one line of training text in the level!<p> +/// During a training session you might want to retrieve a certain line of +/// the level. You can use the member function line(lineNumber) to get a +/// line of the level. +/// The member function count() returns the number of lines in this level. +class KTouchLevelData { + public: + /// Default constructor, creates a default level. + KTouchLevelData() { createDefault(); } + /// Constructor for creating a new level. + KTouchLevelData(const QString& comment, const QString& newChars) + : m_comment(comment), m_newChars(newChars) { createDefault(); } + /// Returns the level description (comment). + const QString& comment() const { return m_comment; } + /// Returns the new characters string. + const QString& newChars() const { return m_newChars; } + /// Returns the line with number 'lineNumber' (0 means first line). + /// + /// If the line number is out of range, the first line will be returned. + const QString& line(unsigned int lineNumber) const; + /// Returns the number of lines. + unsigned int count() const { return m_lines.size(); } + + private: + /// Creates a default level. + void createDefault(); + /// Reads a level from the input stream. + /// @return Returns 'true', when a level could be read successfully, or 'false' otherwise. + bool readLevel(QTextStream& in); + /// Reads a level from a XML Dom Node. + /// @return Returns 'true', when a level could be read successfully, or 'false' otherwise. + bool readLevel(QDomNode in); + /// Writes the level data into the output stream. + void writeLevel(QTextStream& out) const; + /// Writes the level data into the output stream. + void writeLevel(QDomDocument& doc, QDomElement& root) const; + + QString m_comment; ///< A comment for the level. + QString m_newChars; ///< Contains the newly introduced characters in this level. + QValueVector<QString> m_lines; ///< The lines of the level. + + // so I don't have to allow access to read/write function to everybody :-) + friend class KTouchLecture; + friend class KTouchLectureEditor; +}; + +#endif // KTOUCHLEVELDATA_H diff --git a/ktouch/src/ktouchopenrequest.cpp b/ktouch/src/ktouchopenrequest.cpp new file mode 100644 index 00000000..d4bc875b --- /dev/null +++ b/ktouch/src/ktouchopenrequest.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + * ktouchopenrequest.cpp * + * --------------------- * + * Copyright (C) 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 <qradiobutton.h> +#include <qlabel.h> +#include <qbuttongroup.h> + +#include <kpushbutton.h> +#include <klineedit.h> +#include <kcombobox.h> +#include <kfiledialog.h> +#include <klocale.h> +#include <ksqueezedtextlabel.h> +#include <kmessagebox.h> +#include <klocale.h> + +#include "ktouchopenrequest.h" +#include "ktouchopenrequest.moc" + +KTouchOpenRequest::KTouchOpenRequest(QWidget* parent, const char* name, bool modal, WFlags fl) +: KTouchOpenRequestDlg(parent,name, modal,fl) +{ +} + +int KTouchOpenRequest::requestFileToOpen(KURL& url, const QString& caption, const QString& title, + const QString& currentText, const QString& defaultText, const QString& openText, + const QString& newText, KURL current_url, QStringList defaultList, QString emptyListText) +{ + setCaption(caption); + openChoiceGroup->setTitle(title); + currentRadioBtn->setText(currentText); + presetRadioBtn->setText(defaultText); + openFileRadioBtn->setText(openText); + newRadioBtn->setText(newText); + // Fill in current lecture URL or disable if not available + if (current_url.isEmpty()) { + currentLabel->setText(""); + currentRadioBtn->setEnabled(false); + newRadioBtn->setChecked(true); + } + else { + currentLabel->setText(current_url.url()); + currentRadioBtn->setEnabled(true); + currentRadioBtn->setChecked(true); + }; + // Fill preset combo with lecture files from the configuration object + presetCombo->clear(); + if (defaultList.isEmpty()) { + if (emptyListText.isEmpty()) presetCombo->insertItem(i18n("<no default files available>")); + else presetCombo->insertItem(emptyListText); + presetRadioBtn->setEnabled(false); + } + else { + for (QStringList::Iterator it = defaultList.begin(); it != defaultList.end(); ++it ) + presetCombo->insertItem(*it); + presetRadioBtn->setEnabled(true); + } + presetCombo->setCurrentItem(0); + radioBtnChanged(); + + // Finally executre the dialog + int result = exec(); + url = m_url; + return result; +} + +void KTouchOpenRequest::okBtnClicked() { + if (currentRadioBtn->isChecked()) + m_url = currentLabel->text(); + if (presetRadioBtn->isChecked()) + m_url = presetCombo->currentText(); + if (newRadioBtn->isChecked()) + m_url = QString::null; + if (openFileRadioBtn->isChecked()) { + if (openFileEdit->text().isEmpty()) { + KMessageBox::error(this, i18n("Please select or enter a file name.")); + return; + } + KURL tmp = openFileEdit->text(); + if (!tmp.isValid()) { + KMessageBox::error(this, i18n("The URL seems to be malformed; please correct it.")); + return; + } + m_url = tmp; + }; + QDialog::accept(); +} + + +void KTouchOpenRequest::radioBtnChanged() { + if (currentRadioBtn->isChecked()) currentLabel->setEnabled(true); + else currentLabel->setEnabled(false); + if (presetRadioBtn->isChecked()) presetCombo->setEnabled(true); + else presetCombo->setEnabled(false); + if (openFileRadioBtn->isChecked()) { + openFileEdit->setEnabled(true); + browseBtn->setEnabled(true); + } + else { + openFileEdit->setEnabled(false); + browseBtn->setEnabled(false); + } +} + + +void KTouchOpenRequest::browseBtnClicked() { + KURL tmp = KFileDialog::getOpenURL(QString::null, QString::null, this, i18n("Select Training Lecture File") ); + if (!tmp.isEmpty()) + openFileEdit->setText(tmp.url()); +} + + diff --git a/ktouch/src/ktouchopenrequest.h b/ktouch/src/ktouchopenrequest.h new file mode 100644 index 00000000..5c7d78a3 --- /dev/null +++ b/ktouch/src/ktouchopenrequest.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * ktouchopenrequest.h * + * ------------------- * + * Copyright (C) 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. * + ***************************************************************************/ + +#ifndef KTOUCHOPENREQUEST_H +#define KTOUCHOPENREQUEST_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ktouchopenrequest_dlg.h" + +#include <kurl.h> +#include <qstringlist.h> + +/// Dialog for selecting a file to open. +/// +/// This dialog is a costumizable dialog and is to be used for all editors. +/// It offers the choice between a default (current) file, a prepared selection of +/// files, choosing an arbitrary file from somewhere or creating a new file. +/// Before using the dialog you have to set it up. You can do this using the +/// all-in-one function requestFileToOpen() or manually by setting the +/// different widgets yourself. Usually the first proposal will be best, as +/// in the following example: +/// @code +/// KTouchOpenRequest dlg; +/// KURL url; +/// // current_lecture must hold the URL to the current/default lecture or "" +/// // default_lectures must be a QStringList with the default lecture files. +/// int result = dlg.requestFileToOpen(url, i18n("Open a lecture file"), +/// i18n("Which lecture would you like to open?"), i18n("Current lecture:"), +/// i18n("Open a default lecture:"), i18n("Open a lecture file:"), +/// i18n("Create a new lecture!"), current_lecture, +/// default_lectures, i18n("<no lecture files available>")); +/// +/// if (result==QDialog::Accepted) { +/// doSomethingFancyWith(url); +/// } +/// @endcode +/// If you don't use the function and execute the dialog manually, the chosen +/// url is stored in the member variable m_url (if the dialog was accepted by +/// the user). +/// +class KTouchOpenRequest : public KTouchOpenRequestDlg { + Q_OBJECT + + public: + /// Default constructor. + KTouchOpenRequest(QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 ); + /// Default destructor. + ~KTouchOpenRequest() {} + /// Sets up the dialog and runs it. + /// @return Returns the dialogs return code, QDialog::Accepted or QDialog::Rejected. + int requestFileToOpen(KURL& url, const QString& caption, const QString& title, const QString& currentText, + const QString& defaultText, const QString& openText, const QString& newText, + KURL current_url = QString::null, QStringList defaultList = QStringList(), QString emptyListText = QString::null); + + /// After the dialog has been accepted the URL chosen by the user is stored herein. + KURL m_url; + + public slots: + /// Called when user selects Ok (used to check whether the URL is well formed). + void okBtnClicked(); + /// Called when user switches to another radiobutton. + void radioBtnChanged(); + /// Called when the user clicks on the "Browse" button and opens the file select dialog. + void browseBtnClicked(); + +}; + +#endif + diff --git a/ktouch/src/ktouchopenrequest_dlg.ui b/ktouch/src/ktouchopenrequest_dlg.ui new file mode 100644 index 00000000..38ff057e --- /dev/null +++ b/ktouch/src/ktouchopenrequest_dlg.ui @@ -0,0 +1,289 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KTouchOpenRequestDlg</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KTouchOpenRequestDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>436</width> + <height>236</height> + </rect> + </property> + <property name="caption"> + <string>Open Something in Editor</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>openChoiceGroup</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Select File to Open</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>currentRadioBtn</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>Current something</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>presetRadioBtn</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>Preset something:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>presetCombo</cstring> + </property> + </widget> + <widget class="QRadioButton" row="4" column="0"> + <property name="name"> + <cstring>newRadioBtn</cstring> + </property> + <property name="text"> + <string>New something</string> + </property> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <spacer row="5" column="0"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="2" column="1"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit"> + <property name="name"> + <cstring>openFileEdit</cstring> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>browseBtn</cstring> + </property> + <property name="text"> + <string>Browse...</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="KSqueezedTextLabel" row="0" column="1"> + <property name="name"> + <cstring>currentLabel</cstring> + </property> + <property name="text"> + <string>Current something file</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>openFileRadioBtn</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>Open file:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>okBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>cancelBtn</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>okBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>okBtnClicked()</slot> + </connection> + <connection> + <sender>cancelBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>currentRadioBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>radioBtnChanged()</slot> + </connection> + <connection> + <sender>presetRadioBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>radioBtnChanged()</slot> + </connection> + <connection> + <sender>openFileRadioBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>radioBtnChanged()</slot> + </connection> + <connection> + <sender>newRadioBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>radioBtnChanged()</slot> + </connection> + <connection> + <sender>browseBtn</sender> + <signal>clicked()</signal> + <receiver>KTouchOpenRequestDlg</receiver> + <slot>browseBtnClicked()</slot> + </connection> +</connections> +<tabstops> + <tabstop>currentRadioBtn</tabstop> + <tabstop>presetRadioBtn</tabstop> + <tabstop>presetCombo</tabstop> + <tabstop>openFileRadioBtn</tabstop> + <tabstop>browseBtn</tabstop> + <tabstop>newRadioBtn</tabstop> + <tabstop>okBtn</tabstop> + <tabstop>cancelBtn</tabstop> +</tabstops> +<slots> + <slot>okBtnClicked()</slot> + <slot>radioBtnChanged()</slot> + <slot>browseBtnClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>ksqueezedtextlabel.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchprefcolorslayout.ui b/ktouch/src/ktouchprefcolorslayout.ui new file mode 100644 index 00000000..5b475da0 --- /dev/null +++ b/ktouch/src/ktouchprefcolorslayout.ui @@ -0,0 +1,516 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchPrefColorsLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KTouchPrefColorsLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>467</width> + <height>375</height> + </rect> + </property> + <property name="caption"> + <string>Color Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="title"> + <string>Color Scheme</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Select color scheme:</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Black & White</string> + </property> + </item> + <item> + <property name="text"> + <string>Classic</string> + </property> + </item> + <item> + <property name="text"> + <string>Deep Blue</string> + </property> + </item> + <item> + <property name="text"> + <string>Stripy</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_CurrentColorScheme</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Color scheme for displaying the keyboard</string> + </property> + <property name="whatsThis" stdset="0"> + <string>You can choose here the color scheme for displaying the keyboard. <i>Black & White</i> will display all keyboard letters black on white, <i>Classic</i> will colorize the same color for each finger, <i>Deep Blue</i> will display the keyboard in different deep blue colors and <i>Stripy</i> will alternate blue and gray for letters corresponding to same finger.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_CommonTypingLineColors</cstring> + </property> + <property name="text"> + <string>Use common typing line colors independent of color scheme</string> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>colorsGroup</cstring> + </property> + <property name="title"> + <string>Typing Line Colors</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KColorButton" row="0" column="7"> + <property name="name"> + <cstring>kcfg_TeacherBackgroundColor</cstring> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>The background for the teacher's line</string> + </property> + <property name="whatsThis" stdset="0"> + <string>You can change here the background color for the teacher's line (the line of letters you have to type.)</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Typing line</string> + </property> + </widget> + <widget class="KColorButton" row="0" column="4"> + <property name="name"> + <cstring>kcfg_TeacherTextColor</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>The color for the teacher's line</string> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer16</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="0" column="5"> + <property name="name"> + <cstring>spacer11_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KColorButton" row="1" column="4"> + <property name="name"> + <cstring>kcfg_StudentTextColor</cstring> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Change the text color for the student line</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can change the color of the text you type in the student line.</string> + </property> + </widget> + <widget class="QLabel" row="0" column="6"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Background:</string> + </property> + </widget> + <widget class="KColorButton" row="1" column="7"> + <property name="name"> + <cstring>kcfg_StudentBackgroundColor</cstring> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>textLabel1_4_2</cstring> + </property> + <property name="text"> + <string>Text:</string> + </property> + </widget> + <spacer row="1" column="5"> + <property name="name"> + <cstring>spacer5_2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="0" column="3"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Text:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Teacher's line</string> + </property> + </widget> + <spacer row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer16_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>34</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="6"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="text"> + <string>Background:</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_ColorOnError</cstring> + </property> + <property name="text"> + <string>Use different color on error in typing line</string> + </property> + <property name="toolTip" stdset="0"> + <string>If checked this shows your error using the colors below on the typing line</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>140</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>errorTextLabel</cstring> + </property> + <property name="text"> + <string>Text:</string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>kcfg_ErrorTextColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="color"> + <color> + <red>170</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="toolTip" stdset="0"> + <string>Choose the text color for error in typing line</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When you have checked 'Use different color on error in the typing line', you can then choose a color for the text of errors. You make an error when you type a wrong letter in the student line. This is a way to make your errors more visible.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>errorBackgroundLabel</cstring> + </property> + <property name="text"> + <string>Background:</string> + </property> + </widget> + <widget class="KColorButton"> + <property name="name"> + <cstring>kcfg_ErrorBackgroundColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="color"> + <color> + <red>175</red> + <green>0</green> + <blue>55</blue> + </color> + </property> + <property name="toolTip" stdset="0"> + <string>Choose the background color for error in typing line</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When you have checked 'Use different color on error in the typing line', you can then choose a color for the background of errors. You make an error when you type a wrong letter in the student line. This is a way to make your errors more visible.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer17</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<tabstops> + <tabstop>kcfg_TeacherTextColor</tabstop> + <tabstop>kcfg_TeacherBackgroundColor</tabstop> + <tabstop>kcfg_StudentTextColor</tabstop> + <tabstop>kcfg_StudentBackgroundColor</tabstop> + <tabstop>kcfg_ColorOnError</tabstop> + <tabstop>kcfg_ErrorTextColor</tabstop> + <tabstop>kcfg_ErrorBackgroundColor</tabstop> + <tabstop>kcfg_CurrentColorScheme</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchprefgenerallayout.ui b/ktouch/src/ktouchprefgenerallayout.ui new file mode 100644 index 00000000..b62a5e81 --- /dev/null +++ b/ktouch/src/ktouchprefgenerallayout.ui @@ -0,0 +1,341 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchPrefGeneralLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KTouchPrefGeneralLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>485</width> + <height>351</height> + </rect> + </property> + <property name="caption"> + <string>General Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="title"> + <string>Sound</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_BeepOnError</cstring> + </property> + <property name="text"> + <string>&Beep on error</string> + </property> + <property name="toolTip" stdset="0"> + <string>Emit a beep on each typing error</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If checked (default) this will emit a beep each time you make a typing error.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SoundOnLevel</cstring> + </property> + <property name="text"> + <string>&Sound on automatic level change</string> + </property> + <property name="toolTip" stdset="0"> + <string>Play a sound when the level automatically changes.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If this is checked (default) a sound will be played each time you automatically change of level.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Text Line Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>20: Fast</string> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>227</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QSlider" row="0" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_SlidingSpeed</cstring> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>20</height> + </size> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>20</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1</number> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Slow: 1</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Sliding speed:</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Maximum height of sliding line widget:</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>kcfg_MaxSlidingWidgetHeight</cstring> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="minValue"> + <number>50</number> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>in pixels</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>fontTextLabel</cstring> + </property> + <property name="text"> + <string>Font for displayed/typed text:</string> + </property> + </widget> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>114</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KFontRequester" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_Font</cstring> + </property> + <property name="font"> + <font> + <family>Monospace</family> + <pointsize>13</pointsize> + </font> + </property> + <property name="toolTip" stdset="0"> + <string>Change the font for the student and teacher lines</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This button will display a font dialog where you can change the font for the student and the teacher lines. When you leave the font dialog, the new font is previewed in the line below and only applied when you click the Apply button below.</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>kcfg_OverrideLectureFont</cstring> + </property> + <property name="text"> + <string>Override lecture font</string> + </property> + <property name="toolTip" stdset="0"> + <string>If you want to override the font of the training lecture check this box and set your own font.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Most lectures suggest a font to use. This is necessary for not so common languages where characters may be displayed in a strange way and distort the sliding line. If you check this button you can choose your own font and override the lecture font.</string> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>30</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kfontrequester.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchprefkeyboardlayout.ui b/ktouch/src/ktouchprefkeyboardlayout.ui new file mode 100644 index 00000000..34851276 --- /dev/null +++ b/ktouch/src/ktouchprefkeyboardlayout.ui @@ -0,0 +1,142 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchPrefKeyboardLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KTouchPrefKeyboardLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>498</width> + <height>249</height> + </rect> + </property> + <property name="caption"> + <string>Keyboard Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_ShowAnimation</cstring> + </property> + <property name="text"> + <string>Highlight &keys on keyboard</string> + </property> + <property name="toolTip" stdset="0"> + <string>Show on the keyboards the keys you have to type</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This highlights on the keyboard the key you have to type next. It is easier to type when this is checked.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_HideKeyboard</cstring> + </property> + <property name="text"> + <string>Hide keyboard</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_OverrideKeyboardFont</cstring> + </property> + <property name="text"> + <string>Override keyboard fonts</string> + </property> + <property name="toolTip" stdset="0"> + <string>If checked you can specify your own keyboard font instead of the predefined one of the keyboard layout.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Every keyboard layout may specify its own font. If the predefined font of a certain keyboard layout does not work or you would like to have your own, check this button. You may then choose your own font that will be used to draw the characters on the keys.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Font for keys on keyboard:</string> + </property> + </widget> + <widget class="KFontRequester"> + <property name="name"> + <cstring>kcfg_KeyboardFont</cstring> + </property> + </widget> + </vbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer14</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<slots> + <slot>fontChooseBtnClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kfontrequester.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchpreftraininglayout.ui b/ktouch/src/ktouchpreftraininglayout.ui new file mode 100644 index 00000000..19fe985c --- /dev/null +++ b/ktouch/src/ktouchpreftraininglayout.ui @@ -0,0 +1,331 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchPrefTrainingLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KTouchPrefTrainingLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>472</width> + <height>354</height> + </rect> + </property> + <property name="caption"> + <string>Training Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Level Up/Down Limits</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_AutoLevelChange</cstring> + </property> + <property name="text"> + <string>A&utomatic level adjustment</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>l3</cstring> + </property> + <property name="text"> + <string>Correctness:</string> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="2"> + <property name="name"> + <cstring>kcfg_UpSpeedLimit</cstring> + </property> + <property name="maxValue"> + <number>500</number> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>l2</cstring> + </property> + <property name="text"> + <string>Typing speed:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="3"> + <property name="name"> + <cstring>l5</cstring> + </property> + <property name="text"> + <string>%</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>l1</cstring> + </property> + <property name="text"> + <string>Limits to increase a level</string> + </property> + </widget> + <widget class="KIntSpinBox" row="2" column="2"> + <property name="name"> + <cstring>kcfg_UpCorrectLimit</cstring> + </property> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer12</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>l4</cstring> + </property> + <property name="text"> + <string>chars/minute</string> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>l6</cstring> + </property> + <property name="text"> + <string>Limits to decrease a level</string> + </property> + </widget> + <widget class="QLabel" row="2" column="3"> + <property name="name"> + <cstring>l10</cstring> + </property> + <property name="text"> + <string>%</string> + </property> + </widget> + <widget class="KIntSpinBox" row="2" column="2"> + <property name="name"> + <cstring>kcfg_DownCorrectLimit</cstring> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="2"> + <property name="name"> + <cstring>kcfg_DownSpeedLimit</cstring> + </property> + <property name="maxValue"> + <number>500</number> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>l7</cstring> + </property> + <property name="text"> + <string>Typing speed:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>l9</cstring> + </property> + <property name="text"> + <string>chars/minute</string> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer11_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer12_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>l8</cstring> + </property> + <property name="text"> + <string>Correctness:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DisableManualLevelChange</cstring> + </property> + <property name="text"> + <string>Disable manual level change buttons</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox7</cstring> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_RememberLevel</cstring> + </property> + <property name="text"> + <string>&Remember level for next program start</string> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer17</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchslideline.cpp b/ktouch/src/ktouchslideline.cpp new file mode 100644 index 00000000..aa245b4b --- /dev/null +++ b/ktouch/src/ktouchslideline.cpp @@ -0,0 +1,577 @@ +/*************************************************************************** + * ktouchslideline.h * + * ----------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2006 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 "ktouchslideline.h" +#include "ktouchslideline.moc" + +#include <qpainter.h> +#include <qpixmap.h> +#include <kdebug.h> + +#include <cmath> +#include <algorithm> + +#include "prefs.h" +#include "ktouchcolorscheme.h" + +// uncomment the following define to enable extended debugging +//#define SLIDE_LINE_DEBUGGING + + +// --- don't touch the lines below --- +#ifdef SLIDE_LINE_DEBUGGING + +#define DRAW_TEACHER_CURSOR +#define KD_DEBUG(x) kdDebug() << x + +#else // SLIDE_LINE_DEBUGGING + +#define KD_DEBUG(x) ; + +#endif // SLIDE_LINE_DEBUGGING +// --- don't touch the lines above --- + + +KTouchSlideLine::KTouchSlideLine(QWidget *parent) + : QWidget( parent ), + m_teacherPixmap(NULL), + m_studentPixmap(NULL), + m_slideTimer(this), + m_cursorVisible(false), + m_cursorTimer(this) +{ + // set widget defaults (note: teacher and student text is empty after creation) + setMinimumHeight(50); + setMaximumHeight(150); + // NOTE : the maximum widget height is adjusted again in applyPreferences() + + setCursorTimerEnabled(true); + + m_marginCursor = 0; + m_cursorRangeLen = 0; + m_slideLineHeight = 0; + + m_xCharWidth = 0; + m_spaceCharWidth = 0; + m_cursorHeight = 0; + + m_teacherTextLen = 0; + m_correctTextLen = 0; + m_studentTextLen = 0; + m_xCursorTeacher = 0; + m_xCursorStudent = 0; + m_yCursorStudent = 0; + + // the x frame coordinates initially to zero + m_xFrameTeacher = 0; + m_xFrameTeacherCurrent = 0; + m_xFrameStudent = 0; + m_xFrameStudentCurrent = 0; + + connect( &m_cursorTimer, SIGNAL(timeout()), this, SLOT(toggleCursor()) ); + connect( &m_slideTimer, SIGNAL(timeout()), this, SLOT(slide()) ); +} +// ---------------------------------------------------------------------------- + +KTouchSlideLine::~KTouchSlideLine() { + delete m_teacherPixmap; + delete m_studentPixmap; +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::applyPreferences() { + KD_DEBUG( "[KTouchSlideLine::applyPreferences]" << endl ); + + // set maximum widget height (also determines maximum font size) + setMaximumHeight(Prefs::maxSlidingWidgetHeight()); + + // set font face of we override the lecture font + if (Prefs::overrideLectureFont()) + setFont(Prefs::font()); + else + updateSlidingLines(); + + // Note: The function setFont() in turn calls resizeFont() and through that + // the whole widget gets updated. +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::setNewText(const QString& teacher_text, const QString& student_text) { + KD_DEBUG( "[KTouchSlideLine::setNewText]" << endl ); + + m_teacherText=teacher_text; + m_studentText=student_text; + KD_DEBUG( " m_teacherText = '" << m_teacherText << "'" << endl ); + KD_DEBUG( " m_studentText = '" << m_studentText << "'" << endl ); + + updateSlidingLines(); +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::setStudentText(const QString& text) { + KD_DEBUG( "[KTouchSlideLine::setStudentText]" << endl ); + m_studentText=text; + updateStudentLine(); +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::setFont(const QFont& font) { + KD_DEBUG( "[KTouchSlideLine::setFont]" << endl ); + // set the font for the slide line, unless the configuration overrides it + if (Prefs::overrideLectureFont()) m_font = Prefs::font(); + else m_font = font; + + // set point size of the font + resizeFont(); + // Note: this function also takes care of updating the lines +} +// ---------------------------------------------------------------------------- + +bool KTouchSlideLine::canAddCharacter(const QString& new_student_text) { + QFontMetrics fontMetrics( m_font ); + int new_text_len = textLen(fontMetrics, new_student_text); + int allowed_student_length = m_studentPixmap->width() - 2*m_marginCursor; + return (new_text_len <= allowed_student_length); +} +// ---------------------------------------------------------------------------- + + +// *** Public slots *** + +void KTouchSlideLine::setCursorTimerEnabled(bool on) { + if (on) m_cursorTimer.start(600); + else m_cursorTimer.stop(); + m_cursorVisible=false; + drawCursor(); +} +// ---------------------------------------------------------------------------- + + +// *** Private slots *** + +void KTouchSlideLine::toggleCursor() { + m_cursorVisible=!m_cursorVisible; + drawCursor(); +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::slide() { + KD_DEBUG( "[KTouchSlideLine::slide]" << endl ); + if (m_studentPixmap==NULL || m_teacherPixmap==NULL) return; + // calculate new x positions depending on slide speed + double speed = 1.0 + 0.2*Prefs::slidingSpeed(); + + KD_DEBUG( " speed = " << speed << endl ); + + // Teacher's line + double dx_teacher = m_xFrameTeacher - m_xFrameTeacherCurrent; + if (fabs(dx_teacher) < 1.0) { // if we are already close enough, don't slide anylonger + m_xFrameTeacherCurrent = m_xFrameTeacher; + dx_teacher = 0; + } + else { + double dx_teacher_new = dx_teacher/speed; + m_xFrameTeacherCurrent += dx_teacher_new; + } + int int_xFrameTeacherCurrent = static_cast<int>(ceil(m_xFrameTeacherCurrent)); + KD_DEBUG( " m_xFrameTeacher = " << m_xFrameTeacher << " current = " + << int_xFrameTeacherCurrent << " (" << m_xFrameTeacherCurrent << ")" << endl ); + if (!Prefs::right2LeftTyping()) { + bitBlt(this, m_marginHorWidget, m_marginVerWidget, + m_teacherPixmap, int_xFrameTeacherCurrent, 0, m_slideLineWidth, m_slideLineHeight); + } + else { + } + + // Student's line + double dx_student = m_xFrameStudent - m_xFrameStudentCurrent; + if (fabs(dx_student) < 1.0) { // if we are already close enough, don't slide anylonger + m_xFrameStudentCurrent = m_xFrameStudent; + dx_student = 0; + } + else { + double dx_student_new = dx_student/speed; + m_xFrameStudentCurrent += dx_student_new; + } + // NOTE : use ceil() to always round up the value, otherwise we get a funny "jumping" + // curser effect + int int_xFrameStudentCurrent = static_cast<int>(ceil(m_xFrameStudentCurrent)); + KD_DEBUG( " m_xFrameStudent = " << m_xFrameStudent << " current = " + << int_xFrameStudentCurrent << " (" << m_xFrameStudentCurrent << ")" << endl ); + if (!Prefs::right2LeftTyping()) { + bitBlt(this, m_marginHorWidget, m_marginVerWidget + m_slideLineHeight + m_slideLineDist, + m_studentPixmap, int_xFrameStudentCurrent, 0, m_slideLineWidth, m_slideLineHeight); + } + else { + } + + // restart slide timer if necessary + if (dx_teacher != 0 || dx_student != 0) + m_slideTimer.start(100, true); // start singleshot timer to slide again + drawCursor(); +} +// ---------------------------------------------------------------------------- + + +// *** Protected member functions (event implementation) *** + +void KTouchSlideLine::paintEvent(QPaintEvent*) { + KD_DEBUG( "[KTouchSlideLine::paintEvent]" << endl ); + if (m_studentPixmap==NULL || m_teacherPixmap==NULL) + updateSlidingLines(); + else + slide(); +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::resizeEvent ( QResizeEvent * ) { + KD_DEBUG( "[KTouchSlideLine::resizeEvent]" << endl ); + // required input member variables: none + + // when the widget is resized, the whole geometry is invalidated, so we do: + // 1. recalculate and store the geometry of the sliding lines + // 2. resize the font + // 3. recreate the sliding lines (this is done from the resizeFont() function + + // TODO : make these values depending on widget size + m_marginVerWidget = 10; + m_slideLineDist = 5; + m_marginCursor = 20; + m_slideLineHeight = (height() - 2*m_marginVerWidget - m_slideLineDist)/2; + + KD_DEBUG( " m_slideLineDist = " << m_slideLineDist << endl ); + KD_DEBUG( " m_slideLineHeight = " << m_slideLineHeight << endl ); + KD_DEBUG( " m_marginCursor = " << m_marginCursor << endl ); + + // now resize the font + resizeFont(); +} +// ---------------------------------------------------------------------------- + + + +// *** Private member functions (event implementation) + +int KTouchSlideLine::textLen(const QFontMetrics& fontMetrics, const QString& text) { + //KD_DEBUG( "[KTouchSlideLine::textLen]" << endl; + + // required input member variables: m_xCharWidth + + // add an x to the string and subtract the width of the single x afterwards, + // so that in case of "blablabla " still the correct size is returned + int w; + if (!Prefs::right2LeftTyping()) + w = fontMetrics.boundingRect(text + "x").width() - m_xCharWidth; + else + w = fontMetrics.boundingRect("x" + text).width() - m_xCharWidth; + return w; +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::resizeFont() { + KD_DEBUG( "[KTouchSlideLine::resizeFont]" << endl ); + if (m_slideLineHeight == 0) return; // can happen during startup + + // required input member variables: m_slideLineHeight + + // TODO : add support for fixed/overridden font size + + // this formula sets the font height to 65% of the line height + m_font.setPointSize(static_cast<int>(m_slideLineHeight*0.65)); + // set the cursor height + m_cursorHeight = static_cast<int>(m_slideLineHeight*0.65); + KD_DEBUG( " m_cursorHeight = " << m_cursorHeight << endl ); + // calculate the margin between the top of the student line and the top of the cursor. + int y_line_margin = (m_slideLineHeight - m_cursorHeight)/2; + // set the y coordinate of the cursor + m_yCursorStudent = height() - m_marginVerWidget - m_slideLineHeight + y_line_margin; + KD_DEBUG( " m_yCursorStudent = " << m_yCursorStudent << endl ); + // get font infos + QFontMetrics fontMetrics( m_font ); + // width of a single x character + m_xCharWidth = fontMetrics.boundingRect("x").width(); + // width of a single space character + m_spaceCharWidth = fontMetrics.boundingRect("x x").width() - fontMetrics.boundingRect("x x").width(); + // update slide lines + updateSlidingLines(); +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::drawCursor() { + // required input member variables: m_xCursorStudent, m_yCursorStudent, m_cursorHeight + // m_xCursorTeacher + // m_marginHorWidget, m_slideLineDist, m_slideLineHeight + // m_xFrameTeacher, m_xFrameTeacherCurrent, + // m_xFrameStudent, m_xFrameStudentCurrent, + + QPainter p(this); + +#ifdef DRAW_TEACHER_CURSOR + QColor col_tt = Prefs::commonTypingLineColors() ? + Prefs::teacherTextColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_teacherTextColor; + QColor col_tb = Prefs::commonTypingLineColors() ? + Prefs::teacherBackgroundColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_teacherBackground; + + if (m_cursorVisible) p.setPen( col_tt ); + else p.setPen( col_tb ); + + int dx_teacher = static_cast<int>(m_xFrameTeacher - m_xFrameTeacherCurrent); + int cursor_x_teacher = m_marginHorWidget + m_xCursorTeacher + dx_teacher; + + // don't draw cursor if outside frame + if (cursor_x_teacher > m_marginHorWidget && cursor_x_teacher < width() - m_marginHorWidget) { + p.drawLine(cursor_x_teacher, m_yCursorStudent - m_slideLineDist - m_slideLineHeight, + cursor_x_teacher, m_yCursorStudent + m_cursorHeight - m_slideLineDist - m_slideLineHeight); + } +#endif + + + if (m_cursorVisible) p.setPen( m_cursorColor ); + else p.setPen( m_cursorBackground ); + + int dx_student = static_cast<int>(m_xFrameStudent - m_xFrameStudentCurrent); + int cursor_x_student = m_marginHorWidget + m_xCursorStudent + dx_student; + + // don't draw cursor if outside frame + if (cursor_x_student > m_marginHorWidget && cursor_x_student < width() - m_marginHorWidget) { +// KD_DEBUG( " cursor_x_student = " << cursor_x_student << endl ); + p.drawLine(cursor_x_student, m_yCursorStudent, + cursor_x_student, m_yCursorStudent + m_cursorHeight); + } +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::drawEnterChar(QPainter *painter, int cursorPos, int y, int enterWidth) { + // required input member variables: none + + int gap = std::min(2,static_cast<int>(0.2*enterWidth)); + int enterHeight = static_cast<int>(0.4*enterWidth); + int arrowSize = static_cast<int>(enterWidth/4.0); // mind the difference between 4 and 4.0 + painter->drawLine(cursorPos+enterWidth, y, cursorPos+enterWidth, y-enterHeight); // vertical line + painter->drawLine(cursorPos+gap, y, cursorPos+enterWidth, y); // arrow + painter->drawLine(cursorPos+gap, y, cursorPos+gap+arrowSize, y+arrowSize); // arrow + painter->drawLine(cursorPos+gap, y, cursorPos+gap+arrowSize, y-arrowSize); // arrow +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::updateSlidingLines() { + KD_DEBUG( "[KTouchSlideLine::updateSlidingLines]" << endl ); + + // required input member variables: m_teacherText, m_studentText, m_marginCursor, m_cursorRangeLen + // m_slideLineHeight, m_xCharWidth + + // check that input variables are ok, some of this stuff can happen during startup, + // but we MUST NOT allow these variables here + if (m_teacherText.isEmpty()) return; + if (m_slideLineHeight == 0) return; + + // first update some variables + QFontMetrics fontMetrics( m_font ); + m_teacherTextLen = textLen(fontMetrics, m_teacherText); + KD_DEBUG( " m_teacherTextLen = " << m_teacherTextLen << endl ); + + // assume long text first and calculate variables for maximum slide line size + m_marginHorWidget = 30; // TODO : make dependent of widget width + m_slideLineWidth = width() - 2*m_marginHorWidget; + m_cursorRangeLen = m_slideLineWidth - 2*m_marginCursor; + + // adjust m_marginCursor and m_cursorRangeLen if text is smaller then available widget width + if (m_teacherTextLen < m_cursorRangeLen) { + KD_DEBUG( " --> short text, correcting variables" << endl ); + m_cursorRangeLen = m_teacherTextLen; + m_slideLineWidth = m_cursorRangeLen + 2*m_marginCursor; + m_marginHorWidget = (width() - m_slideLineWidth)/2; + } + KD_DEBUG( " m_cursorRangeLen = " << m_cursorRangeLen << endl ); + KD_DEBUG( " m_slideLineWidth = " << m_slideLineWidth << endl ); + KD_DEBUG( " m_marginHorWidget = " << m_marginHorWidget << endl ); + + // delete old pixmaps because we have to change their sizes anyway + delete m_teacherPixmap; + m_teacherPixmap = NULL; // just a precaution + delete m_studentPixmap; + m_studentPixmap = NULL; // just a precaution + + // create the teacher pixmap + int w = 2*m_marginCursor + m_teacherTextLen; // TODO : add size of enter character + int h = m_slideLineHeight; + m_teacherPixmap = new QPixmap(w,h); + KD_DEBUG( " m_teacherPixmap = " << w << " x " << h << endl ); + + // draw the teacher pixmap text + QPainter painter; + painter.begin (m_teacherPixmap, this); + painter.setFont( m_font ); + + QColor col_tt = Prefs::commonTypingLineColors() ? + Prefs::teacherTextColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_teacherTextColor; + QColor col_tb = Prefs::commonTypingLineColors() ? + Prefs::teacherBackgroundColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_teacherBackground; + + painter.fillRect( m_teacherPixmap->rect(), QBrush(col_tb) ); + painter.setPen( col_tt ); + // create a rectangle for the text drawing + QRect textRect(m_marginCursor, 0, w-2*m_marginCursor, h); + // left aligned + if (!Prefs::right2LeftTyping()) { + painter.drawText(textRect, QPainter::AlignLeft | QPainter::AlignVCenter, m_teacherText); + // TODO : draw enter char after the text + } + // right aligned, e.g. for Hebrew text + else { + painter.drawText(textRect, QPainter::AlignRight | QPainter::AlignVCenter, m_teacherText); + // TODO : draw enter character at left of text + } + painter.end(); + // done with the teachers text which will not change so quickly again + + // create student line pixmap thats slightly longer than the teacher's pixmap + m_studentPixmap = new QPixmap(w+100,h); + KD_DEBUG( " m_studentPixmap = " << w+100 << " x " << h << endl ); + + // update (draw) student line and calculate coordinates needed for sliding + repaint(true); // trigger a paint event to erase the background + updateStudentLine(); +} +// ---------------------------------------------------------------------------- + +void KTouchSlideLine::updateStudentLine() { + KD_DEBUG( "[KTouchSlideLine::updateStudentLine]" << endl ); + if (m_teacherPixmap == NULL || m_studentPixmap == NULL) return; + + // required input member variables: m_teacherText, m_studentText, m_marginCursor, m_cursorRangeLen + // m_slideLineHeight, m_xCharWidth, m_teacherTextLen + + if (m_teacherTextLen == 0) return; + + QFontMetrics fontMetrics( m_font ); + m_studentTextLen = textLen(fontMetrics, m_studentText); + KD_DEBUG( " m_studentTextLen = " << m_studentTextLen << endl ); + + // we now need to find out, how much of the students text was typed correctly + int teacherLen = m_teacherText.length(); + int studentLen = m_studentText.length(); + + unsigned int min_len = QMIN(teacherLen, studentLen); + + QString correctText; + if (!Prefs::right2LeftTyping()) { + // for text typed from left to right + for (unsigned int i=0; i<min_len; ++i) { + if (m_teacherText[i] == m_studentText[i]) correctText += m_teacherText[i]; + else break; + } + } + else { + // for text typed from right to left + for (unsigned int i=min_len; i>0; --i) { + if (m_teacherText[i-1] == m_studentText[i-1]) correctText = m_teacherText[i] + correctText; + else break; + } + } + + bool error = (correctText.length() != min_len); + KD_DEBUG( " error in text? = " << error << endl ); + KD_DEBUG( " correctText = '" << correctText << "'" << endl ); + m_correctTextLen = textLen(fontMetrics, correctText); + KD_DEBUG( " m_correctTextLen = " << m_correctTextLen << endl ); + + // *** Teacher slide line *** + + // adjust some of the coordinates + // NOTE: use ceil to get consistent rounding + m_xCursorTeacher = static_cast<int>(m_marginCursor + ceil(double(m_correctTextLen)/m_teacherTextLen * m_cursorRangeLen)); + KD_DEBUG( " m_xCursorTeacher = " << m_xCursorTeacher << endl ); + + // TODO : transform coordinates when Prefs::right2LeftTyping() is true + + // find the coordinates of the current position in the Teacher's pixmap + m_xCursorTPixmap = m_marginCursor + m_correctTextLen; + KD_DEBUG( " m_xCursorTPixmap = " << m_xCursorTPixmap << endl ); + + // find the left x coordinates of the frame to be copied from the teacher pixmap, but first store the shift. + m_xFrameTeacher = m_xCursorTPixmap - m_xCursorTeacher; + KD_DEBUG( " m_xFrameTeacher = " << m_xFrameTeacher << endl ); + + + // *** Student slide line *** + + // make sure our student text length is < then allowed length + int allowed_student_length = m_studentPixmap->width() - 2*m_marginCursor; + if (m_studentTextLen > allowed_student_length) { + KD_DEBUG( " WARNING : m_studentTextLen ("<< m_studentTextLen <<") > allowed_student_length ("<< allowed_student_length <<")" << endl ); + m_studentTextLen = allowed_student_length; + } + + // adjust some of the coordinates + if (m_studentTextLen > m_teacherTextLen) + m_xCursorStudent = m_marginCursor + m_cursorRangeLen; + else + m_xCursorStudent = static_cast<int>(m_marginCursor + ceil(double(m_studentTextLen)/m_teacherTextLen * m_cursorRangeLen)); + KD_DEBUG( " m_xCursorStudent = " << m_xCursorStudent << endl ); + + // find the coordinates of the current position in the Students's pixmap + m_xCursorSPixmap = m_marginCursor + m_studentTextLen; + + // find the left x coordinates of the frame to be copied from the teacher pixmap, but first store the shift. + m_xFrameStudent = m_xCursorSPixmap - m_xCursorStudent; + KD_DEBUG( " m_xFrameTeacher = " << m_xFrameStudent << endl ); + + // now let's draw the students pixmap + QPainter painter; + painter.begin (m_studentPixmap, this); + if (Prefs::colorOnError()) { + // draw the student line depending on the colour settings + if (error) { + // determine colors depending on preferences settings + m_cursorBackground = Prefs::commonTypingLineColors() ? Prefs::errorBackgroundColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_errorBackground; + m_cursorColor = Prefs::commonTypingLineColors() ? Prefs::errorTextColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_errorTextColor; + } + else { + // determine colors depending on preferences settings + m_cursorBackground = Prefs::commonTypingLineColors() ? Prefs::studentBackgroundColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_studentBackground; + m_cursorColor = Prefs::commonTypingLineColors() ? Prefs::studentTextColor() : + KTouchColorScheme::m_colorSchemes[Prefs::currentColorScheme()].m_studentTextColor; + } + painter.fillRect (m_studentPixmap->rect(), QBrush(m_cursorBackground)); + painter.setPen( m_cursorColor ); + } + else { + // use always student text colors + m_cursorColor = Prefs::studentTextColor(); + painter.setPen( m_cursorColor ); + m_cursorBackground = Prefs::studentBackgroundColor(); + painter.fillRect( m_studentPixmap->rect(), QBrush(m_cursorBackground) ); + } + // draw the text + painter.setFont( m_font ); + QRect textRect(m_marginCursor, 0, m_studentPixmap->width()-2*m_marginCursor, m_studentPixmap->height()); + if (Prefs::right2LeftTyping()) + painter.drawText(textRect, QPainter::AlignRight | QPainter::AlignVCenter, m_studentText); + else + painter.drawText(textRect, QPainter::AlignLeft | QPainter::AlignVCenter, m_studentText); + painter.end(); + // done painting the students line + + // turn on cursor blinking + m_cursorVisible = true; + m_cursorTimer.start(800); + + slide(); // start the sliding +} diff --git a/ktouch/src/ktouchslideline.h b/ktouch/src/ktouchslideline.h new file mode 100644 index 00000000..bb6f7a1e --- /dev/null +++ b/ktouch/src/ktouchslideline.h @@ -0,0 +1,156 @@ +/*************************************************************************** + * ktouchslideline.h * + * ----------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2006 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. * + ***************************************************************************/ + +#ifndef KTOUCHSLIDELINE_H +#define KTOUCHSLIDELINE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qwidget.h> +#include <qtimer.h> +class QPixmap; +class QPainter; + +/// This widget just displays the sliding text. +/// +/// KTouchSlideLine is a fire-and-forget widget, so you don't have to care about any +/// drawing details. After the initial setup (setting the font, colours etc.) you simple +/// need to call setNewText() when the teacher text changes or setStudentText() when +/// only the student line has changed. Everything else (including the +/// choice of the background colour) is done by the widget. +/// +/// Please refer to the documentation 'sliding_line_widget.pdf' in the doc folder. +class KTouchSlideLine : public QWidget { + Q_OBJECT + public: + /// Constructor + KTouchSlideLine(QWidget *parent); + /// Destructor, free memory allocated for the line pixmap. + ~KTouchSlideLine(); + /// Applies the preferences (font and colours). + /// This function calls setFont() which in turn results in a complete update + /// of the widget and its sliding lines. + /// @see setFont() + void applyPreferences(); + /// Sets the teacher and student text (usually called when a new line is started). + /// This function calls updateSlidingLines(). + /// @see updateSlidingLines() + void setNewText(const QString& teacher_text, const QString& student_text); + /// Sets the student text (called whenever the student string changes). + /// This function calls updateStudentLine(). + /// @see updateStudentLine() + void setStudentText(const QString& text); + /// Changes the font of the slide line widget and updates everything else. + /// This function is called when the font is changed. It updates the size + /// of the sliding lines and the font size accordingly. Then it updates + /// the widget. + /// This function calls resizeFont(). + /// @see resizeFont() + void setFont(const QFont& font); + + /// Returns true when the character can be added without problems. + /// This function checks if the new possibly wrong student line + /// would still fit into the student line pixmap and returns + /// false if not. + bool canAddCharacter(const QString& new_student_text); + + public slots: + /// Starts or stops the cursor blinking timer. + void setCursorTimerEnabled(bool on); + + private slots: + /// Turns the cursor on or off (this function triggered by the cursor timer). + void toggleCursor(); + /// Slides the lines into position (this function is triggered by the sliding timer). + void slide(); + + protected: + /// Simply updates the widget. + /// This function calls updateSlidingLines() if necessary, otherwise just slide(). + /// @see updateSlidingLines() + /// @see slide() + void paintEvent( QPaintEvent * ); + /// Will be called when the widget is resized. + /// This event will first recalculate the geometry of the sliding lines. + /// Then the font size will be updated and through that, the sliding lines will be + /// updated. + void resizeEvent ( QResizeEvent * ); + + private: + /// Calculates the correct text length (in pixels) taking trailing spaces into account + int textLen(const QFontMetrics& fontMetrics, const QString& text); + /// Will recalculate the font size and related variables depending on the height of the widget. + /// This function calls updateSlidingLines(). + /// @see updateSlidingLines() + void resizeFont(); + /// Just draws the cursor (if visible) + void drawCursor(); + /// Draws the "enter" character at the given position (y is the y-position of the arrow). + void drawEnterChar(QPainter *painter, int cursorPos, int y, int enterWidth); + /// Recreates the student and teacher pixmaps and updates all related variables. + /// In this function the teacher pixmap is drawn it will not be modified until the next + /// call of updateSlidingLines(). This function also calls updateStudentLine(). + /// @see updateStudentLine() + void updateSlidingLines(); + /// Redraws the student line alone and updates all related variables. + void updateStudentLine(); + + QFont m_font; ///< The font for the sliding lines. + QString m_teacherText; ///< The teachers text. + QString m_studentText; ///< The students text. + QPixmap *m_teacherPixmap; ///< Pixmap used to draw the teacher sliding line, created in updateSlidingLines(). + QPixmap *m_studentPixmap; ///< Pixmap used to draw the student sliding line, created in updateSlidingLines(). + + QTimer m_slideTimer; ///< This is the timer for the sliding of the lines. + + bool m_cursorVisible; ///< Flag which indicates the current state of the cursor. + QTimer m_cursorTimer; ///< This is the cursor on/off timer. + QColor m_cursorColor; ///< Defines the colour of the cursor (when turned on). + QColor m_cursorBackground; ///< Defines the background colour of the cursor (when turned off). + + // variables depending on size of widget, will be updated in resizeEvent() + int m_marginVerWidget; ///< Vertical margin between widget boundary and sliding line in pixels. + int m_slideLineDist; ///< Vertical distance between sliding lines in pixels. + int m_slideLineHeight; ///< Height of a sliding line in pixels. + int m_marginCursor; ///< The margin for cursor movement (minimum distance from left and right ends of slide line boundaries). + + // variables depending on font face and size, will be updated in the resizeFont() function + int m_xCharWidth; ///< The width (in pixel) of a small x (used to calculate space width and other stuff). + int m_spaceCharWidth; ///< The width of a space character (in pixel). + int m_cursorHeight; ///< Height of the cursor. + int m_yCursorStudent; ///< Cursor top y coordinate (from top of visible area) in the student line. + + // newly defined variables, will be updated on the setNewText() and partially in setStudentText() + int m_marginHorWidget; ///< Horizontal margin between widget boundary and sliding line in pixels. + int m_slideLineWidth; ///< The length of the sliding line in pixels. + int m_cursorRangeLen; ///< Length (in pixel) that the cursor can move between left and right margin. + + int m_teacherTextLen; ///< The total length of the teacher line (in pixel) WITHOUT enter character. + int m_correctTextLen; ///< The length of correctly typed text so far (in pixel). + int m_studentTextLen; ///< The total length of text typed (potentially wrong) in the student line (in pixel). + + int m_xCursorTeacher; ///< Cursor position (from left side of visible area) in the teacher line. + int m_xCursorTPixmap; ///< Current cursor position in the teacher pixmap (from left in pixels). + int m_xCursorStudent; ///< Cursor position (from left side of visible area) in the student line. + int m_xCursorSPixmap; ///< Current cursor position in the student pixmap (from left in pixels). + + + int m_xFrameTeacher; ///< The final X-position (position after slide is done) for the copy-frame in the teachers pixmap. + double m_xFrameTeacherCurrent; ///< The current X-position (position after slide is done) for the copy-frame in the teachers pixmap. + + int m_xFrameStudent; ///< The final X-position (position after slide is done) for the copy-frame in the students pixmap. + double m_xFrameStudentCurrent; ///< The current X-position (position after slide is done) for the copy-frame in the students pixmap. +}; + +#endif // KTOUCHSLIDELINE_H diff --git a/ktouch/src/ktouchstatistics.cpp b/ktouch/src/ktouchstatistics.cpp new file mode 100644 index 00000000..97b1d45c --- /dev/null +++ b/ktouch/src/ktouchstatistics.cpp @@ -0,0 +1,475 @@ +/*************************************************************************** + * ktouchstatistics.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 "ktouchstatistics.h" +#include "ktouchstatistics.moc" + +#include <list> +#include <vector> +#include <utility> + +#include <qprogressbar.h> +#include <qlcdnumber.h> +#include <qlabel.h> +#include <qradiobutton.h> +#include <qbuttongroup.h> + +#include <kpushbutton.h> +#include <kcombobox.h> +#include <kurl.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kdebug.h> +#include <ktabwidget.h> + +#include "ktouch.h" +#include "ktouchchartwidget.h" + +KTouchStatistics::KTouchStatistics(QWidget* parent) + : KTouchStatisticsDlg(parent) +{ + sessionsRadio->setChecked(true); + WPMRadio->setChecked(true); + eventRadio->setChecked(true); + connect(closeButton, SIGNAL(clicked()), this, SLOT(accept()) ); + connect(lectureCombo, SIGNAL(activated(int)), this, SLOT(lectureActivated(int)) ); + connect(clearButton, SIGNAL(clicked()), this, SLOT(clearHistory()) ); + // connect the radio buttons with the chart update function + connect(buttonGroup1, SIGNAL(clicked(int)), this, SLOT(updateChartTab()) ); + connect(buttonGroup2, SIGNAL(clicked(int)), this, SLOT(updateChartTab()) ); + connect(buttonGroup3, SIGNAL(clicked(int)), this, SLOT(updateChartTab()) ); + + // TODO : temporarily remove detailed stats page and deactivate options + levelsRadio->setEnabled(false); +} +// ---------------------------------------------------------------------------- + +void KTouchStatistics::run(const KURL& currentLecture, const KTouchStatisticsData& stats, + const KTouchLevelStats& currLevelStats, + const KTouchSessionStats& currSessionStats) +{ +// kdDebug() << "[KTouchStatistics::run]" << endl; +// kdDebug() << " currentLecture = '" << currentLecture << "'" << endl; + + // fill lecture combo with data + // loop over all lecturestatistics + lectureCombo->clear(); + QMapConstIterator<KURL, KTouchLectureStats> it = stats.m_lectureStats.begin(); + m_currentIndex = 0; + while (it != stats.m_lectureStats.end()) { + QString t = it.data().m_lectureTitle; + // if current lecture, remember index and adjust text + if (it.key() == currentLecture || + currentLecture.isEmpty() && it.key().url()=="default") + { + m_currentIndex = lectureCombo->count(); + if (t == "default") t = i18n("Default level..."); + lectureLabel1->setText(t); + lectureLabel2->setText(t); + t = i18n("***current*** ") + t; + } + lectureCombo->insertItem(t); + ++it; + } + if (lectureCombo->count()==0) { + // this shouldn't happen if the dialog is run with proper data + KMessageBox::information(this, i18n("No statistics data available yet!")); + return; + } + + // remember stats + m_allStats = stats; + m_currLevelStats = currLevelStats; + m_currSessionStats = currSessionStats; + // modify current lecture entry + lectureCombo->setCurrentItem(m_currentIndex); + lectureActivated(m_currentIndex); + m_lectureIndex = m_currentIndex; + + // update the current tabs with current session/level data + updateCurrentSessionTab(); + updateCurrentLevelTab(); + // set current session as current tab + tabWidget->showPage(currentTab); + exec(); +} +// ---------------------------------------------------------------------------- + +void KTouchStatistics::lectureActivated(int index) { + if (m_allStats.m_lectureStats.count()==0) { + // TODO : Reset all tabs to "empty" look + m_lectureIndex = 0; + return; + } + if (index >= static_cast<int>(m_allStats.m_lectureStats.count())) { + kdDebug() << "Lecture index out of range: " << index << " >= " << m_allStats.m_lectureStats.count() << endl; + return; + } + m_lectureIndex = index; + //kdDebug() << "Lecture stats changed: " << it.data().m_lectureTitle << endl; + // update the tabs + updateChartTab(); +} +// ---------------------------------------------------------------------------- + +void KTouchStatistics::clearHistory() { + if (KMessageBox::warningContinueCancel(this, i18n("Erase all statistics data for the current user?"),QString::null,KStdGuiItem::del()) + == KMessageBox::Continue) + { + KTouchPtr->clearStatistics(); // clear statistics data in KTouch + // clear and reset local copy + m_allStats.clear(); + QString s = lectureCombo->text(m_currentIndex); + lectureCombo->clear(); + lectureCombo->insertItem(s); + m_currentIndex = 0; + lectureCombo->setCurrentItem(m_currentIndex); + lectureActivated(m_currentIndex); + updateChartTab(); + } +} +// ---------------------------------------------------------------------------- + + +void KTouchStatistics::updateCurrentSessionTab() { + // session/level/info + QString levelnums; + int last_level = -2; + int levels_count = 0; + std::set<unsigned int>::iterator last_it = m_currSessionStats.m_levelNums.end(); + --last_it; + for (std::set<unsigned int>::iterator it = m_currSessionStats.m_levelNums.begin(); + it != m_currSessionStats.m_levelNums.end(); ++it) + { + // do we have a level number that is not a subsequent level of the + // previous? + + if ((static_cast<unsigned int>(last_level + 1) != *it) || + (it == last_it)) + { + if (it != m_currSessionStats.m_levelNums.begin()) { + if (levels_count > 1) levelnums += "..."; + else levelnums += ","; + } + levels_count = 0; + levelnums += QString("%1").arg(*it+1); + + } + else { + ++levels_count; // increase level count + } + last_level = *it; + } + levelLabel1->setText(levelnums); + // general stats group + elapsedTimeLCD->display(static_cast<int>(m_currSessionStats.m_elapsedTime)); + totalCharsLCD->display(static_cast<int>(m_currSessionStats.m_totalChars) ); + wrongCharsLCD->display(static_cast<int>(m_currSessionStats.m_totalChars-m_currSessionStats.m_correctChars) ); + wordsLCD->display(static_cast<int>(m_currSessionStats.m_words) ); + // typing rate group + if (m_currSessionStats.m_elapsedTime == 0) { + charSpeedLCD->display(0); + wordSpeedLCD->display(0); + } + else { + charSpeedLCD->display(static_cast<int>(m_currSessionStats.m_correctChars/m_currSessionStats.m_elapsedTime*60.0) ); + wordSpeedLCD->display(static_cast<int>(m_currSessionStats.m_words/m_currSessionStats.m_elapsedTime*60.0) ); + } + // accuracy + correctnessBar->setProgress(static_cast<int>( + (100.0*m_currSessionStats.m_correctChars)/m_currSessionStats.m_totalChars) ); + // create sorted list of missed characters + std::list<KTouchCharStats> charList( m_currSessionStats.m_charStats.begin(), m_currSessionStats.m_charStats.end()); + charList.sort(higher_miss_hit_ratio); + + std::list<KTouchCharStats>::const_iterator it=charList.begin(); + unsigned int i=0; + for (; i<8 && it!=charList.end(); ++i, ++it) { + if (it->missHitRatio()==0) + break; // stop listing keys when their hit-miss-ration is zero + switch (i) { + case 0 : charLabel1->setText( it->m_char ); charProgress1->setEnabled(true); + charProgress1->setProgress( it->missHitRatio() ); break; + case 1 : charLabel2->setText( it->m_char ); charProgress2->setEnabled(true); + charProgress2->setProgress( it->missHitRatio() ); break; + case 2 : charLabel3->setText( it->m_char ); charProgress3->setEnabled(true); + charProgress3->setProgress( it->missHitRatio() ); break; + case 3 : charLabel4->setText( it->m_char ); charProgress4->setEnabled(true); + charProgress4->setProgress( it->missHitRatio() ); break; + case 4 : charLabel5->setText( it->m_char ); charProgress5->setEnabled(true); + charProgress5->setProgress( it->missHitRatio() ); break; + case 5 : charLabel6->setText( it->m_char ); charProgress6->setEnabled(true); + charProgress6->setProgress( it->missHitRatio() ); break; + case 6 : charLabel7->setText( it->m_char ); charProgress7->setEnabled(true); + charProgress7->setProgress( it->missHitRatio() ); break; + case 7 : charLabel8->setText( it->m_char ); charProgress8->setEnabled(true); + charProgress8->setProgress( it->missHitRatio() ); break; + } + } + // set remaining char labels and progress bars to zero + for(; i<8; ++i) { + switch (i) { + case 0 : charLabel1->setText(" "); charProgress1->setProgress(0); charProgress1->setEnabled(false); break; + case 1 : charLabel2->setText(" "); charProgress2->setProgress(0); charProgress2->setEnabled(false); break; + case 2 : charLabel3->setText(" "); charProgress3->setProgress(0); charProgress3->setEnabled(false); break; + case 3 : charLabel4->setText(" "); charProgress4->setProgress(0); charProgress4->setEnabled(false); break; + case 4 : charLabel5->setText(" "); charProgress5->setProgress(0); charProgress5->setEnabled(false); break; + case 5 : charLabel6->setText(" "); charProgress6->setProgress(0); charProgress6->setEnabled(false); break; + case 6 : charLabel7->setText(" "); charProgress7->setProgress(0); charProgress7->setEnabled(false); break; + case 7 : charLabel8->setText(" "); charProgress8->setProgress(0); charProgress8->setEnabled(false); break; + } + } +} +// ---------------------------------------------------------------------------- + +void KTouchStatistics::updateCurrentLevelTab() { + // level info + levelLabel2->setText( QString("%1").arg(m_currLevelStats.m_levelNum+1) ); + // general stats group + elapsedTimeLCDLevel->display(static_cast<int>(m_currLevelStats.m_elapsedTime)); + totalCharsLCDLevel->display(static_cast<int>(m_currLevelStats.m_totalChars) ); + wrongCharsLCDLevel->display(static_cast<int>(m_currLevelStats.m_totalChars-m_currLevelStats.m_correctChars) ); + wordsLCDLevel->display(static_cast<int>(m_currLevelStats.m_words) ); + // typing rate group + if (m_currLevelStats.m_elapsedTime == 0) { + charSpeedLCDLevel->display(0); + wordSpeedLCDLevel->display(0); + } + else { + charSpeedLCDLevel->display(static_cast<int>(m_currLevelStats.m_correctChars/m_currLevelStats.m_elapsedTime*60.0) ); + wordSpeedLCDLevel->display(static_cast<int>(m_currLevelStats.m_words/m_currLevelStats.m_elapsedTime*60.0) ); + } + // accuracy + correctnessBarLevel->setProgress(static_cast<int>( + (100.0*m_currLevelStats.m_correctChars)/m_currLevelStats.m_totalChars) ); + // create sorted list of missed characters + std::list<KTouchCharStats> charList( m_currLevelStats.m_charStats.begin(), m_currLevelStats.m_charStats.end()); + charList.sort(higher_miss_hit_ratio); + + std::list<KTouchCharStats>::const_iterator it=charList.begin(); + unsigned int i=0; + for (; i<8 && it!=charList.end(); ++i, ++it) { + if (it->missHitRatio()==0) + break; // stop listing keys when their hit-miss-ration is zero + switch (i) { + case 0 : charLabel1Level->setText( it->m_char ); charProgress1->setEnabled(true); + charProgress1Level->setProgress( it->missHitRatio() ); break; + case 1 : charLabel2Level->setText( it->m_char ); charProgress2->setEnabled(true); + charProgress2Level->setProgress( it->missHitRatio() ); break; + case 2 : charLabel3Level->setText( it->m_char ); charProgress3->setEnabled(true); + charProgress3Level->setProgress( it->missHitRatio() ); break; + case 3 : charLabel4Level->setText( it->m_char ); charProgress4->setEnabled(true); + charProgress4Level->setProgress( it->missHitRatio() ); break; + case 4 : charLabel5Level->setText( it->m_char ); charProgress5->setEnabled(true); + charProgress5Level->setProgress( it->missHitRatio() ); break; + case 5 : charLabel6Level->setText( it->m_char ); charProgress6->setEnabled(true); + charProgress6Level->setProgress( it->missHitRatio() ); break; + case 6 : charLabel7Level->setText( it->m_char ); charProgress7->setEnabled(true); + charProgress7Level->setProgress( it->missHitRatio() ); break; + case 7 : charLabel8Level->setText( it->m_char ); charProgress8->setEnabled(true); + charProgress8Level->setProgress( it->missHitRatio() ); break; + } + } + // set remaining char labels and progress bars to zero + for(; i<8; ++i) { + switch (i) { + case 0 : charLabel1Level->setText(" "); charProgress1Level->setProgress(0); charProgress1Level->setEnabled(false); break; + case 1 : charLabel2Level->setText(" "); charProgress2Level->setProgress(0); charProgress2Level->setEnabled(false); break; + case 2 : charLabel3Level->setText(" "); charProgress3Level->setProgress(0); charProgress3Level->setEnabled(false); break; + case 3 : charLabel4Level->setText(" "); charProgress4Level->setProgress(0); charProgress4Level->setEnabled(false); break; + case 4 : charLabel5Level->setText(" "); charProgress5Level->setProgress(0); charProgress5Level->setEnabled(false); break; + case 5 : charLabel6Level->setText(" "); charProgress6Level->setProgress(0); charProgress6Level->setEnabled(false); break; + case 6 : charLabel7Level->setText(" "); charProgress7Level->setProgress(0); charProgress7Level->setEnabled(false); break; + case 7 : charLabel8Level->setText(" "); charProgress8Level->setProgress(0); charProgress8Level->setEnabled(false); break; + } + } +} +// ---------------------------------------------------------------------------- + +void KTouchStatistics::updateChartTab() { + // remove all current chart objects + chartWidget->clearObjectList(); + // if no lecture data is available, return + if (m_allStats.m_lectureStats.count()==0 || m_lectureIndex >= m_allStats.m_lectureStats.count()) return; + // what kind of chart is required? + if (levelsRadio->isChecked()) { + // TODO : nothing yet + } + else { + // find correct lecture index + QMapConstIterator<KURL, KTouchLectureStats> it = m_allStats.m_lectureStats.begin(); + int index = m_lectureIndex; + while (index-- > 0) ++it; + std::vector< std::pair<double, double> > data; + QString caption = "Session data"; + switch (buttonGroup2->selectedId()) { + case 0 : // words per minute + // loop over all session data entries in *it and store words per minute data + for (QValueVector<KTouchSessionStats>::const_iterator session_it = (*it).m_sessionStats.begin(); + session_it != (*it).m_sessionStats.end(); ++session_it) + { + double t = (*session_it).m_elapsedTime; + double wpm = (t == 0) ? 0 : (*session_it).m_words/t*60.0; + double tp = (*session_it).m_timeRecorded.toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, wpm) ); + } + // add current session if selected lecture matches + if (m_currentIndex == m_lectureIndex && + m_currSessionStats.m_elapsedTime != 0) + { + double t = m_currSessionStats.m_elapsedTime; + double wpm = m_currSessionStats.m_words/t*60.0; + double tp = QDateTime::currentDateTime().toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, wpm) ); + } + chartWidget->LeftAxis.setLabel( i18n("Words per minute") ); + chartWidget->LeftAxis.setLabelFormat(0, 'f', 0); + break; + + + case 1 : // chars per minute + // loop over all session data entries in *it and store chars per minute data + for (QValueVector<KTouchSessionStats>::const_iterator session_it = (*it).m_sessionStats.begin(); + session_it != (*it).m_sessionStats.end(); ++session_it) + { + double t = (*session_it).m_elapsedTime; + double cpm = (t == 0) ? 0 : (*session_it).m_correctChars/t*60.0; + double tp = (*session_it).m_timeRecorded.toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, cpm) ); + } + // add current session + if (m_currentIndex == m_lectureIndex && + m_currSessionStats.m_elapsedTime != 0) + { + double t = m_currSessionStats.m_elapsedTime; + double cpm = m_currSessionStats.m_correctChars/t*60.0; + double tp = QDateTime::currentDateTime().toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, cpm) ); + } + chartWidget->LeftAxis.setLabel( i18n("Characters per minute") ); + chartWidget->LeftAxis.setLabelFormat(0, 'f', 0); + break; + + + case 2 : // correctness + // loop over all session data entries in *it and store correctness data + for (QValueVector<KTouchSessionStats>::const_iterator session_it = (*it).m_sessionStats.begin(); + session_it != (*it).m_sessionStats.end(); ++session_it) + { + double tc = (*session_it).m_totalChars; + double corr = (tc == 0) ? 0 : (*session_it).m_correctChars/tc; + double tp = (*session_it).m_timeRecorded.toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, corr) ); + } + // add current session + if (m_currentIndex == m_lectureIndex && + m_currSessionStats.m_totalChars != 0) + { + double tc = m_currSessionStats.m_totalChars; + double corr = m_currSessionStats.m_correctChars/tc; + double tp = QDateTime::currentDateTime().toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, corr) ); + } + chartWidget->LeftAxis.setLabel( i18n("Correctness") ); + chartWidget->LeftAxis.setLabelFormat(0, 'g', 1); + break; + + + case 3 : // skill + // loop over all session data entries in *it and store correctness data + for (QValueVector<KTouchSessionStats>::const_iterator session_it = (*it).m_sessionStats.begin(); + session_it != (*it).m_sessionStats.end(); ++session_it) + { + double tc = (*session_it).m_totalChars; + double corr = (tc == 0) ? 0 : (*session_it).m_correctChars/tc; + double t = (*session_it).m_elapsedTime; + double cpm = (t == 0) ? 0 : (*session_it).m_correctChars/t*60.0; + double skill = corr*cpm; + double tp = (*session_it).m_timeRecorded.toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, skill) ); + } + // add current session + if (m_currentIndex == m_lectureIndex && + m_currSessionStats.m_totalChars != 0 && + m_currSessionStats.m_elapsedTime != 0) + { + double tc = m_currSessionStats.m_totalChars; + double corr = m_currSessionStats.m_correctChars/tc; + double t = m_currSessionStats.m_elapsedTime; + double cpm = m_currSessionStats.m_correctChars/t*60.0; + double skill = corr*cpm; + double tp = QDateTime::currentDateTime().toTime_t()/(3600.0*24); + data.push_back(std::make_pair(tp, skill) ); + } + chartWidget->LeftAxis.setLabel( i18n("Skill") ); + chartWidget->LeftAxis.setLabelFormat(0, 'f', 0); + break; + + default : return; + } + // Create plot object for session statistics + KPlotObject * ob; + if (data.size() <= 1) return; + ob = new KPlotObject(caption, "red", KPlotObject::CURVE, 2, KPlotObject::SOLID); + + // Add some random points to the plot object + double min_x = 1e20; + double max_x = -1; + double max_y = -1; + for (unsigned int i=0; i<data.size(); ++i) { + double x; + if (timeRadio->isChecked()) { + x = data[i].first - data[0].first; + chartWidget->BottomAxis.setLabel( i18n( "Time since first practice session in days" )); + } + else { + x = i+1; + chartWidget->BottomAxis.setLabel( i18n( "Sessions" )); + } + ob->addPoint( DPoint(x, data[i].second) ); + min_x = std::min(x, min_x); + max_x = std::max(x, max_x); + max_y = std::max(data[i].second, max_y); + } + if (max_y == 0) max_y = 1; + max_y *= 1.1; + chartWidget->setLimits( min_x, max_x, -0.1*max_y, max_y ); +// kdDebug() << min_x << " " << max_x << " " << max_y << endl; + // Add plot object to chart + chartWidget->addObject(ob); + } +} +// ---------------------------------------------------------------------------- + +/* + +void KTouchStatistics::updateChartTab() { + if (m_trainer->m_sessionHistory.size()<2) { + chartTypeButtonGroup->setEnabled(false); + chartWidget->setEraseColor( Qt::gray ); + } + else { + chartTypeButtonGroup->setEnabled(true); + chartWidget->setEraseColor( Qt::white ); + }; + if (charSpeedButton->isChecked()) + chartWidget->setChartType( KTouchChartWidget::CharsPerMinute ); + else if (wordSpeedButton->isChecked()) + chartWidget->setChartType( KTouchChartWidget::WordsPerMinute ); + else if (accuracyButton->isChecked()) + chartWidget->setChartType( KTouchChartWidget::Accuracy ); + else if (timeButton->isChecked()) + chartWidget->setChartType( KTouchChartWidget::ElapsedTime ); +} +// ---------------------------------------------------------------------------- + +*/ diff --git a/ktouch/src/ktouchstatistics.h b/ktouch/src/ktouchstatistics.h new file mode 100644 index 00000000..549dfc3c --- /dev/null +++ b/ktouch/src/ktouchstatistics.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * ktouchstatistics.h * + * ------------------ * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHSTATISTICS_H +#define KTOUCHSTATISTICS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ktouchstatistics_dlg.h" +#include "ktouchstatisticsdata.h" + +class QShowEvent; +class KTouch; +class KURL; + +/// Implementation of the statistics widget +class KTouchStatistics : public KTouchStatisticsDlg { + Q_OBJECT + public: + /// Constructor, takes a pointer to the trainer object. + KTouchStatistics(QWidget* parent); + + /// Fills the statistics dialog with data and executes it. + void run(const KURL& currentLecture, const KTouchStatisticsData& stats, + const KTouchLevelStats& currLevelStats, + const KTouchSessionStats& currSessionStats); + + public slots: + /// Called whenever the lecture is changed. + void lectureActivated(int index); + /// Clears the statistics data. + void clearHistory(); + /// Updates the chart with current lecture and settings + void updateChartTab(); + + private: + /// Updates the data in the current tab, using the data stored in m_currSessionStats. + void updateCurrentSessionTab(); + /// Updates the data in the current tab, using the data stored in m_currLevelStats. + void updateCurrentLevelTab(); + + unsigned int m_currentIndex; ///< Index in lecture combo of the current lecture. + unsigned int m_lectureIndex; ///< Index of currently selected lecture. + KTouchStatisticsData m_allStats; ///< Contains all lecture statistics. + KTouchLevelStats m_currLevelStats; ///< Contains current level stats (not yet stored in allStats). + KTouchSessionStats m_currSessionStats; ///< Contains current session stats (not yet stored in allStats). +}; + +#endif // KTOUCHSTATISTICS_H diff --git a/ktouch/src/ktouchstatistics_dlg.ui b/ktouch/src/ktouchstatistics_dlg.ui new file mode 100644 index 00000000..761fd908 --- /dev/null +++ b/ktouch/src/ktouchstatistics_dlg.ui @@ -0,0 +1,1287 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchStatisticsDlg</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KTouchStatisticsDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>606</width> + <height>600</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Training Statistics</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>currentTab</cstring> + </property> + <attribute name="title"> + <string>Current Training Session</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>currentTitleLabel</cstring> + </property> + <property name="font"> + <font> + <pointsize>20</pointsize> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Current Training Session Statistics</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel4_3_2</cstring> + </property> + <property name="text"> + <string>Levels in this session:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>levelLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>levelLabel1</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2_4_2</cstring> + </property> + <property name="text"> + <string>Lecture:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>lectureLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>lectureLabel1</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout28</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="title"> + <string>General Statistics</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Words typed:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Mistyped characters:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Time (seconds) elapsed:</string> + </property> + </widget> + <widget class="QLCDNumber" row="0" column="1"> + <property name="name"> + <cstring>elapsedTimeLCD</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>This shows the number of seconds you have typed in this training session.</string> + </property> + </widget> + <widget class="QLCDNumber" row="1" column="1"> + <property name="name"> + <cstring>totalCharsLCD</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>The number of characters (correct and wrong) that you have typed so far.</string> + </property> + </widget> + <widget class="QLCDNumber" row="2" column="1"> + <property name="name"> + <cstring>wrongCharsLCD</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>Shows how many wrong characters you did type.</string> + </property> + </widget> + <widget class="QLCDNumber" row="3" column="1"> + <property name="name"> + <cstring>wordsLCD</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>The number of words typed so far.</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Characters typed:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox7</cstring> + </property> + <property name="title"> + <string>Character Statistics (characters you need to focus on)</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Here the characters are listed that you have missed during the typing test. The percentage shows the ratio of how often a key was missed versus how often the key was pressed in total. Large numbers indicate that the key was missed a lot.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QFrame" row="0" column="2" rowspan="4" colspan="1"> + <property name="name"> + <cstring>frame4</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="frameShape"> + <enum>VLine</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + </widget> + <widget class="QProgressBar" row="0" column="1"> + <property name="name"> + <cstring>charProgress1</cstring> + </property> + </widget> + <widget class="QProgressBar" row="0" column="4"> + <property name="name"> + <cstring>charProgress5</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>charLabel1</cstring> + </property> + <property name="text"> + <string>a:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="3"> + <property name="name"> + <cstring>charLabel5</cstring> + </property> + <property name="text"> + <string>e:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>charLabel2</cstring> + </property> + <property name="text"> + <string>b:</string> + </property> + </widget> + <widget class="QProgressBar" row="1" column="4"> + <property name="name"> + <cstring>charProgress6</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>charLabel6</cstring> + </property> + <property name="text"> + <string>f:</string> + </property> + </widget> + <widget class="QProgressBar" row="1" column="1"> + <property name="name"> + <cstring>charProgress2</cstring> + </property> + </widget> + <widget class="QProgressBar" row="2" column="1"> + <property name="name"> + <cstring>charProgress3</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="3"> + <property name="name"> + <cstring>charLabel7</cstring> + </property> + <property name="text"> + <string>g:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>charLabel3</cstring> + </property> + <property name="text"> + <string>c:</string> + </property> + </widget> + <widget class="QProgressBar" row="2" column="4"> + <property name="name"> + <cstring>charProgress7</cstring> + </property> + </widget> + <widget class="QProgressBar" row="3" column="4"> + <property name="name"> + <cstring>charProgress8</cstring> + </property> + </widget> + <widget class="QProgressBar" row="3" column="1"> + <property name="name"> + <cstring>charProgress4</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="3"> + <property name="name"> + <cstring>charLabel8</cstring> + </property> + <property name="text"> + <string>h:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>charLabel4</cstring> + </property> + <property name="text"> + <string>d:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="0" column="1"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="title"> + <string>Typing Rate</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Characters per minute:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel6_2</cstring> + </property> + <property name="text"> + <string>Words per minute:</string> + </property> + </widget> + <widget class="QLCDNumber" row="0" column="1"> + <property name="name"> + <cstring>charSpeedLCD</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>The number of correct characters you typed per minute.</string> + </property> + </widget> + <widget class="QLCDNumber" row="1" column="1"> + <property name="name"> + <cstring>wordSpeedLCD</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>The number of words you typed per minute.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="1" column="1"> + <property name="name"> + <cstring>groupBox6</cstring> + </property> + <property name="title"> + <string>Accuracy</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QProgressBar" row="0" column="0"> + <property name="name"> + <cstring>correctnessBar</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The ratio of correctly typed characters versus total number of characters typed.</string> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer22</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>currentLevelTab</cstring> + </property> + <attribute name="title"> + <string>Current Level Statistics</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>currentTitleLabel_3</cstring> + </property> + <property name="font"> + <font> + <pointsize>20</pointsize> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Current Level Statistics</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>lectureLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>lectureLabel2</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel4_3</cstring> + </property> + <property name="text"> + <string>Level:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2_4</cstring> + </property> + <property name="text"> + <string>Lecture:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>levelLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>levelLabel2</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout28_2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>groupBox4_2</cstring> + </property> + <property name="title"> + <string>General Statistics</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4_2</cstring> + </property> + <property name="text"> + <string>Words typed:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel5_2</cstring> + </property> + <property name="text"> + <string>Mistyped characters:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="text"> + <string>Time (seconds) elapsed:</string> + </property> + </widget> + <widget class="QLCDNumber" row="0" column="1"> + <property name="name"> + <cstring>elapsedTimeLCDLevel</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + </widget> + <widget class="QLCDNumber" row="1" column="1"> + <property name="name"> + <cstring>totalCharsLCDLevel</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + </widget> + <widget class="QLCDNumber" row="2" column="1"> + <property name="name"> + <cstring>wrongCharsLCDLevel</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + </widget> + <widget class="QLCDNumber" row="3" column="1"> + <property name="name"> + <cstring>wordsLCDLevel</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_3</cstring> + </property> + <property name="text"> + <string>Characters typed:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox7_2</cstring> + </property> + <property name="title"> + <string>Character Statistics (characters you need to focus on)</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Here the characters are listed that you have missed during the typing test. The percentage shows the ratio of how often a key was missed versus how often the key was pressed correctly. Large numbers indicate that the key was missed a lot.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QFrame" row="0" column="2" rowspan="4" colspan="1"> + <property name="name"> + <cstring>frame4_2</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="frameShape"> + <enum>VLine</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + </widget> + <widget class="QProgressBar" row="0" column="1"> + <property name="name"> + <cstring>charProgress1Level</cstring> + </property> + </widget> + <widget class="QProgressBar" row="0" column="4"> + <property name="name"> + <cstring>charProgress5Level</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>charLabel1Level</cstring> + </property> + <property name="text"> + <string>a:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="3"> + <property name="name"> + <cstring>charLabel5Level</cstring> + </property> + <property name="text"> + <string>e:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>charLabel2Level</cstring> + </property> + <property name="text"> + <string>b:</string> + </property> + </widget> + <widget class="QProgressBar" row="1" column="4"> + <property name="name"> + <cstring>charProgress6Level</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>charLabel6Level</cstring> + </property> + <property name="text"> + <string>f:</string> + </property> + </widget> + <widget class="QProgressBar" row="1" column="1"> + <property name="name"> + <cstring>charProgress2Level</cstring> + </property> + </widget> + <widget class="QProgressBar" row="2" column="1"> + <property name="name"> + <cstring>charProgress3Level</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="3"> + <property name="name"> + <cstring>charLabel7Level</cstring> + </property> + <property name="text"> + <string>g:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>charLabel3Level</cstring> + </property> + <property name="text"> + <string>c:</string> + </property> + </widget> + <widget class="QProgressBar" row="2" column="4"> + <property name="name"> + <cstring>charProgress7Level</cstring> + </property> + </widget> + <widget class="QProgressBar" row="3" column="4"> + <property name="name"> + <cstring>charProgress8Level</cstring> + </property> + </widget> + <widget class="QProgressBar" row="3" column="1"> + <property name="name"> + <cstring>charProgress4Level</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="3"> + <property name="name"> + <cstring>charLabel8Level</cstring> + </property> + <property name="text"> + <string>h:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>charLabel4Level</cstring> + </property> + <property name="text"> + <string>d:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="0" column="1"> + <property name="name"> + <cstring>groupBox5_2</cstring> + </property> + <property name="title"> + <string>Typing Rate</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel6_3</cstring> + </property> + <property name="text"> + <string>Characters per minute:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel6_2_2</cstring> + </property> + <property name="text"> + <string>Words per minute:</string> + </property> + </widget> + <widget class="QLCDNumber" row="0" column="1"> + <property name="name"> + <cstring>charSpeedLCDLevel</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + </widget> + <widget class="QLCDNumber" row="1" column="1"> + <property name="name"> + <cstring>wordSpeedLCDLevel</cstring> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>32767</height> + </size> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="1" column="1"> + <property name="name"> + <cstring>groupBox6_3</cstring> + </property> + <property name="title"> + <string>Accuracy</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QProgressBar" row="0" column="0"> + <property name="name"> + <cstring>correctnessBarLevel</cstring> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer22_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>89</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>progressTab</cstring> + </property> + <attribute name="title"> + <string>Monitor Progress</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>currentTitleLabel_2</cstring> + </property> + <property name="font"> + <font> + <pointsize>20</pointsize> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Your Typing Progress</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Show progress in lecture:</string> + </property> + </widget> + <widget class="KComboBox"> + <property name="name"> + <cstring>lectureCombo</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can select the lecture you want to obtain statistics for.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="KTouchChartWidget"> + <property name="name"> + <cstring>chartWidget</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>2</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox6_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Progress Chart Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Chart Data</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>levelsRadio</cstring> + </property> + <property name="text"> + <string>Level stats</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>sessionsRadio</cstring> + </property> + <property name="text"> + <string>Session stats</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QButtonGroup" row="0" column="1"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Chart Type</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>WPMRadio</cstring> + </property> + <property name="text"> + <string>Words per minute</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>CPMRadio</cstring> + </property> + <property name="text"> + <string>Characters per minute</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>correctRadio</cstring> + </property> + <property name="text"> + <string>Correctness</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>skillRadio</cstring> + </property> + <property name="text"> + <string>Skill</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QButtonGroup" row="0" column="2"> + <property name="name"> + <cstring>buttonGroup3</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Time Axis Scaling</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>timeRadio</cstring> + </property> + <property name="text"> + <string>Time scaled</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>eventRadio</cstring> + </property> + <property name="text"> + <string>Constant spacing</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </grid> + </widget> + </vbox> + </widget> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>clearButton</cstring> + </property> + <property name="text"> + <string>Clear History</string> + </property> + <property name="toolTip" stdset="0"> + <string>Pressing this button will erase the whole training statistics for the current user.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KTouchChartWidget</class> + <header location="local">ktouchchartwidget.h</header> + <sizehint> + <width>200</width> + <height>200</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>3</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1525">89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af4000005bc494441545885b596416823d719c77f0e0a8c0e0b12b860810533a0057bd88035642133908087e4a29ea2b087684fb1c9253db57b6b4ba125c9a15d07127613ca3a7bc93a812e524f514a4b9f601b3f85763d0e6d7676a1c623b0f00c8989442ca281085e0e23c9abeccab10dd1e5cfa7ff7fbef7f17fdffbde9b9152729a9ff3c7b2e249601e683f84df01518c7cab3e739a7c4f9c76f1e5175cd45f42767f2f27f0da1fae41ea34d9ce500003b86c97a10fc63963022b4f9560f053178046bd558734888e98c00f831aa0fdc4050cc0d61d36bedac0c81a63fcedfdd779bf179ec981999336a1f3ebb262a0b1fc2b93d75ffc0d624fd09d8d119f49c24c4cf77e4cfc5e07882106f94eed44cd78620734ad84fe4a96f8e90e6bff5b030dea77ebc4f3314431994517ede52cdacb39e8c5274d7bf2be8d6341743b4dfa1c68cf77a8b56a68058dce4e8ca643bc23a00df15f4378d03d7101277640bef9e18cf8ddfb33c1bb45d29fe4585d588528a654709385ff0ff1ed101e4448294f3c0b4e75726dc7517d60b53547358ac9bd92a3f185a0fb51cceaed80abbd98e6291687a103eb37d6d55cacaba59eadec595bf57b4ad9b3f651dcedabddc6be626e0ee4d75c7ee31a57e7aee0ffa98fd62873ed992b5cfdfb3ebcbd8efd9cab36ffe54d7eff50bea5595b6d363695e3382ab1564a565e7d4d6dca2da5be566aebdf09def3ee25f110d7ff7c4ba94b2b6acbdb52eacba16ef707fa7da5fe79a1a8befdeff0bb7d95e87f90ef5b5f29fb695b49298f7ac02958c82f2456c682cfc12c98f8918f55302102d77091c458058bfedd3eb9410eb123b0162c8812bd17f9b8291722b0e64daa8d2a967ec48ff3b58f36ff0948e697d7f6710a0e1f3ff818162088024cdd248802d041ec099c94461005a40b69bcae87bbe08ef9200ab0741381205d003ff271175dfcb63fe647f9d2058807c3a39a6cc18a52df28a57695524aa97b7e82a358ed2ab57f675fa94b9547fe9f405f29b564abcd3b5bc7ea36ef6ca92bcf5c39da020d0dbf1d8001e15e88b998e028c6001148483dfaff04164010e3e8d6b13a47b7900379b4053160160c082197cf3d16ddf30e92e97c2e9f4b7a85a4077e4cf7480f84510839e81ff61f8bb5ed3ace603adf3fec431e0402e67f44a71ff5402a712026d3cd50fdac8a356b210291747d20710c07bfe763e78ad0af430b36b63770f2ce981781a0b458421c087e0e543fade2165c6adbb5313fca572a96a8efd493d332728001d477ea94164af8b14fe562053ff6295f2ce3c73ece9c43fd7e1d7a1d444b502e9627f8cac50a5ed7c39977905188756821db72821fe9655b621d5ad4bb7560781dafdf5857cebc8377e0e1ea2ea225707407d99238f30ea22558fc6a91dcce4d4ab3650492d200825e17139b6ae63e2e207a5d8c94cbdaf990d24209d112b8ba8b6ccb713e5777b9d9b849fd6f75a4943353df03b73eb8a5aefff23a5ed7c39ab7f0da93281e48363ed8c05ab2f8c5a5d547f8116efca3caf5f7d6587b6bedb177c4d4dbb074e125e8825518262b587090c4fe818f7bc1219bc95259aee01ff85873c9c41bf123fd65fb25346dfa536d6a018d9648cef5e7024333f0a364a2f9918fa92763d5c646b69b98ba49f56e150a933c3ac8c89bbaf8b1052ceb2e7e04aeeea2451aa66e8e67fa089b3471f5e4dc97164af82d7f82274aee98e3de8ac73a6016202024fd429a200ae13c04ed214621a4a01949380f91d6c57cca3ce287e8b5bd635f1dc738604304869183bd2938887175673abf47721b9ecd8126e4210c3b9087ce101f8eedd432b22da7f2e4939e38bb0321e47259e84076880fc7cd4183e26c110e1fcf7308e69c79ac03536b6bb41a38070eb54f6ad8039bc6a0c1726a99e6a089abb9f83ff301a86dd7707b2eb5ffd4c6bc9d4af4e54c192f7dc61e28668a881d41e56285201fb0f2ec0a413ea0f26c053f978ce7ce4107e349032ff226f891de3be761680651144d2d60ea245cbfb1aea270fa870c407c2a282e15c966b25365e141c8f6ddeda94ff5ef018e80fecc25cb29330000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>ktouchchartwidget.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/ktouch/src/ktouchstatisticsdata.cpp b/ktouch/src/ktouchstatisticsdata.cpp new file mode 100644 index 00000000..42251df2 --- /dev/null +++ b/ktouch/src/ktouchstatisticsdata.cpp @@ -0,0 +1,553 @@ +/*************************************************************************** + * ktouchstatisticsdata.cpp * + * ------------------------ * + * Copyright (C) 2005 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 "config.h" +#include "ktouchstatisticsdata.h" + +#include <qfile.h> +#include <qstringlist.h> +#include <qdom.h> + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kio/netaccess.h> + +#ifdef HAVE_SSTREAM +# include <sstream> +#else +# include <strstream> +#endif + +// remove this define if XML output should be made uncompressed +#define COMPRESSED_XML_STATISTICS + + +// *** KTouchCharStats *** + +int KTouchCharStats::missHitRatio() const { + if (m_correctCount+m_wrongCount == 0) return 0; + return static_cast<int>((static_cast<double>(m_wrongCount)/(m_correctCount+m_wrongCount)*100)); +} +// ---------------------------------------------------------------------------- + +QTextStream& operator<<(QTextStream &out, const KTouchCharStats &ch) { + return (out << ch.m_char.unicode() << " " << ch.m_correctCount << " " << ch.m_wrongCount); +} +// ---------------------------------------------------------------------------- + + + +// *** KTouchLevelStats *** + +void KTouchLevelStats::clear() { + m_levelNum = -1; + m_elapsedTime = 0; + m_words = 0; + m_totalChars = 0; + m_correctChars = 0; + m_charStats.clear(); +} +// ---------------------------------------------------------------------------- + +bool KTouchLevelStats::read(QDomNode in) { + // read the level number + QDomNode n = in.attributes().namedItem("Number"); + bool ok; + if (!n.isNull()) { + m_levelNum = n.nodeValue().toInt(&ok); + if (!ok) { + m_levelNum = -1; + return false; // need level number! + } + } + // read time + n = in.attributes().namedItem("Time"); + if (!n.isNull()) { + m_elapsedTime = n.nodeValue().toDouble(&ok); + if (!ok) m_elapsedTime = 0; + } + // read total chars + n = in.attributes().namedItem("Chars"); + if (!n.isNull()) { + m_totalChars = n.nodeValue().toInt(&ok); + if (!ok) m_totalChars = 0; + } + // read correct chars + n = in.attributes().namedItem("Corrects"); + if (!n.isNull()) { + m_correctChars = n.nodeValue().toInt(&ok); + if (!ok) m_correctChars = 0; + } + // read number of words + n = in.attributes().namedItem("Words"); + if (!n.isNull()) { + m_words = n.nodeValue().toInt(&ok); + if (!ok) m_words = 0; + } + // done with attributes, read recording time + n = in.namedItem("Time"); + if (!n.isNull()) { + QString timestring = n.firstChild().nodeValue(); + if (timestring != QString::null) + m_timeRecorded = QDateTime::fromString(timestring, Qt::ISODate); + } + // read characters + n = in.namedItem("CharStats"); + if (!n.isNull()) { + QString char_data = n.firstChild().nodeValue(); + //kdDebug() << "'" << char_data << "'" << endl; +#ifdef HAVE_SSTREAM + std::stringstream strm(std::string(char_data.local8Bit())); +#else + std::istrstream strm(char_data.local8Bit(), char_data.local8Bit().length()); +#endif + int ch, correct, wrong; + while (strm >> ch >> correct >> wrong) + m_charStats.insert( KTouchCharStats(QChar(ch), correct, wrong) ); + //kdDebug() << m_charStats.size() << endl; + } + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchLevelStats::write(QDomDocument& doc, QDomElement& root) const { + QDomElement level = doc.createElement("LevelStats"); + root.appendChild(level); + level.setAttribute("Number", m_levelNum); + level.setAttribute("Time", m_elapsedTime); + level.setAttribute("Chars", m_totalChars); + level.setAttribute("Corrects", m_correctChars); + level.setAttribute("Words", m_words); + // add time + QDomElement e = doc.createElement("Time"); + QDomText tn = doc.createTextNode(m_timeRecorded.toString(Qt::ISODate)); + e.appendChild(tn); + level.appendChild(e); + // add char stats + QString char_data; + // we append for each missed char the char-unicode and the counters + for (std::set<KTouchCharStats>::const_iterator it=m_charStats.begin(); it!=m_charStats.end(); ++it) + char_data += QString(" %1 %2 %3").arg(it->m_char.unicode()) + .arg(it->m_correctCount).arg(it->m_wrongCount); + e = doc.createElement("CharStats"); + tn = doc.createTextNode(char_data); + e.appendChild(tn); + level.appendChild(e); +} +// ---------------------------------------------------------------------------- + +void KTouchLevelStats::addCorrectChar(QChar key) { + ++m_correctChars; + ++m_totalChars; + // we only count non-space characters + if (key!=QChar(' ')) { + std::set<KTouchCharStats>::iterator it = m_charStats.find( KTouchCharStats(key,0,0) ); + if (it==m_charStats.end()) + m_charStats.insert( KTouchCharStats(key,1,0) ); + else + ++(const_cast<KTouchCharStats&>(*it).m_correctCount); + // this const_cast is necessary because the current gcc 3.2 has a bug in the + // implementation of set::find(). The non-const overload is missing and find() always + // returns a const object. Maybe in the next release this will be fixed. Until then + // the const_cast helps. + } +} +// ---------------------------------------------------------------------------- + +void KTouchLevelStats::removeCorrectChar() { + --m_correctChars; +} +// ---------------------------------------------------------------------------- + +void KTouchLevelStats::addWrongChar(QChar key) { + ++m_totalChars; + if (key==QChar(8) || key==QChar(' ')) + return; // don't remember wrong backspaces or spaces + std::set<KTouchCharStats>::iterator it = m_charStats.find( KTouchCharStats(key,0,0) ); + if (it==m_charStats.end()) + m_charStats.insert( KTouchCharStats(key,0,1) ); + else + ++(const_cast<KTouchCharStats&>(*it).m_wrongCount); + // this const_cast is necessary because the current gcc 3.2 has a bug in the + // implementation of set::find(). The non-const overload is missing and find() always + // returns a const object. Maybe in the next release this will be fixed. Until then + // the const_cast helps. +} +// ---------------------------------------------------------------------------- + +double KTouchLevelStats::correctness() const { + return (m_totalChars==0) ? 1.0 : static_cast<double>(m_correctChars)/m_totalChars; +} +// ---------------------------------------------------------------------------- + +double KTouchLevelStats::charSpeed() const { + return (m_elapsedTime==0) ? 0 : 60.0*m_correctChars/m_elapsedTime; +} +// ---------------------------------------------------------------------------- + +double KTouchLevelStats::wordSpeed() const { + return (m_elapsedTime==0) ? 0 : 60.0*m_words/m_elapsedTime; +} +// ---------------------------------------------------------------------------- + + + +// *** KTouchSessionStats *** + +void KTouchSessionStats::clear() { + m_elapsedTime = 0; + m_words = 0; + m_totalChars = 0; + m_correctChars = 0; + m_levelNums.clear(); + m_charStats.clear(); +} +// ---------------------------------------------------------------------------- + +bool KTouchSessionStats::read(QDomNode in) { + // read time + QDomNode n = in.attributes().namedItem("Time"); + bool ok; + if (!n.isNull()) { + m_elapsedTime = n.nodeValue().toDouble(&ok); + if (!ok) m_elapsedTime = 0; + } + // read total chars + n = in.attributes().namedItem("Chars"); + if (!n.isNull()) { + m_totalChars = n.nodeValue().toInt(&ok); + if (!ok) m_totalChars = 0; + } + // read correct chars + n = in.attributes().namedItem("Corrects"); + if (!n.isNull()) { + m_correctChars = n.nodeValue().toInt(&ok); + if (!ok) m_correctChars = 0; + } + // read number of words + n = in.attributes().namedItem("Words"); + if (!n.isNull()) { + m_words = n.nodeValue().toInt(&ok); + if (!ok) m_words = 0; + } + // done with attributes, read recording time + n = in.namedItem("Time"); + if (!n.isNull()) { + QString timestring = n.firstChild().nodeValue(); + if (timestring != QString::null) + m_timeRecorded = QDateTime::fromString(timestring, Qt::ISODate); + } + // read level numbers + n = in.namedItem("LevelNums"); + if (!n.isNull()) { + QString str = n.firstChild().nodeValue(); +#ifdef HAVE_SSTREAM + std::stringstream strm(std::string(str.local8Bit())); +#else + std::istrstream strm(str.local8Bit(), str.local8Bit().length()); +#endif + int l; + while (strm >> l) + m_levelNums.insert(l); + } + // read characters + n = in.namedItem("CharStats"); + if (!n.isNull()) { + QString char_data = n.firstChild().nodeValue(); + //kdDebug() << "'" << char_data << "'" << endl; +#ifdef HAVE_SSTREAM + std::stringstream strm(std::string(char_data.local8Bit())); +#else + std::istrstream strm(char_data.local8Bit(), char_data.local8Bit().length()); +#endif + int ch, correct, wrong; + while (strm >> ch >> correct >> wrong) + m_charStats.insert( KTouchCharStats(QChar(ch), correct, wrong) ); + //kdDebug() << m_charStats.size() << endl; + } + + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchSessionStats::write(QDomDocument& doc, QDomElement& root) const { + QDomElement session = doc.createElement("SessionStats"); + root.appendChild(session); + session.setAttribute("Time", m_elapsedTime); + session.setAttribute("Chars", m_totalChars); + session.setAttribute("Corrects", m_correctChars); + session.setAttribute("Words", m_words); + // add time + QDomElement e = doc.createElement("Time"); + QDomText tn = doc.createTextNode(m_timeRecorded.toString(Qt::ISODate)); + e.appendChild(tn); + session.appendChild(e); + // add levels + QString level_nums; + for (std::set<unsigned int>::const_iterator it = m_levelNums.begin(); it != m_levelNums.end(); ++it) + level_nums += QString( " %1").arg(*it); + e = doc.createElement("LevelNums"); + tn = doc.createTextNode(level_nums); + e.appendChild(tn); + session.appendChild(e); + // add char data + QString char_data; + // we append for each missed char the char-unicode and the counters + for (std::set<KTouchCharStats>::const_iterator it=m_charStats.begin(); it!=m_charStats.end(); ++it) + char_data += QString(" %1 %2 %3").arg(it->m_char.unicode()) + .arg(it->m_correctCount).arg(it->m_wrongCount); + e = doc.createElement("CharStats"); + tn = doc.createTextNode(char_data); + e.appendChild(tn); + session.appendChild(e); +} +// ---------------------------------------------------------------------------- + +void KTouchSessionStats::addCorrectChar(QChar key) { + ++m_correctChars; + ++m_totalChars; + // we only count non-space characters + if (key!=QChar(' ')) { + std::set<KTouchCharStats>::iterator it = m_charStats.find( KTouchCharStats(key,0,0) ); + if (it==m_charStats.end()) + m_charStats.insert( KTouchCharStats(key,1,0) ); + else + ++(const_cast<KTouchCharStats&>(*it).m_correctCount); + // this const_cast is necessary because the current gcc 3.2 has a bug in the + // implementation of set::find(). The non-const overload is missing and find() always + // returns a const object. Maybe in the next release this will be fixed. Until then + // the const_cast helps. + } +} +// ---------------------------------------------------------------------------- + +void KTouchSessionStats::removeCorrectChar() { + --m_correctChars; +} +// ---------------------------------------------------------------------------- + +void KTouchSessionStats::addWrongChar(QChar key) { + ++m_totalChars; + if (key==QChar(8) || key==QChar(' ')) + return; // don't remember wrong backspaces or spaces + std::set<KTouchCharStats>::iterator it = m_charStats.find( KTouchCharStats(key,0,0) ); + if (it==m_charStats.end()) + m_charStats.insert( KTouchCharStats(key,0,1) ); + else + ++(const_cast<KTouchCharStats&>(*it).m_wrongCount); + // this const_cast is necessary because the current gcc 3.2 has a bug in the + // implementation of set::find(). The non-const overload is missing and find() always + // returns a const object. Maybe in the next release this will be fixed. Until then + // the const_cast helps. +} +// ---------------------------------------------------------------------------- + + + +// *** KTouchLectureStats *** + +void KTouchLectureStats::clear() { + m_lectureTitle = QString::null; + m_lectureURL = QString::null; + m_levelStats.clear(); + m_sessionStats.clear(); +} +// ---------------------------------------------------------------------------- + +bool KTouchLectureStats::read(QDomNode in) { + clear(); + // read the URL + QDomNode n = in.namedItem("URL"); + if (!n.isNull()) m_lectureURL = n.firstChild().nodeValue(); + else return false; // no URL, statistics useless + // read the Title + n = in.namedItem("Title"); + if (!n.isNull()) m_lectureTitle = n.firstChild().nodeValue(); + // read the level stats + n = in.namedItem("AllLevelStats"); + if (!n.isNull()) { + // loop over all childs and read levels + QDomNode levelNode = n.firstChild(); + while (!levelNode.isNull()) { + //kdDebug() << "[KTouchLectureStats::read] Reading level stats" << endl; + KTouchLevelStats tmp; + if (tmp.read(levelNode)) m_levelStats.push_back(tmp); + levelNode = levelNode.nextSibling(); + } + } + // read the session stats + n = in.namedItem("AllSessionStats"); + if (!n.isNull()) { + // loop over all childs and read sessions + QDomNode sessionNode = n.firstChild(); + while (!sessionNode.isNull()) { + //kdDebug() << "[KTouchLectureStats::read] Reading session stats" << endl; + KTouchSessionStats tmp; + if (tmp.read(sessionNode)) m_sessionStats.push_back(tmp); + sessionNode = sessionNode.nextSibling(); + } + } + return true; +} +// ---------------------------------------------------------------------------- + +void KTouchLectureStats::write(QDomDocument& doc, QDomElement& root) const { + if (m_lectureURL.isEmpty()) return; // Error here! + QDomElement lecture = doc.createElement("LectureStats"); + root.appendChild(lecture); + // store lecture URL + QDomElement urlElement = doc.createElement("URL"); + QDomText urlText = doc.createTextNode(m_lectureURL.url()); + urlElement.appendChild(urlText); + lecture.appendChild(urlElement); + // store title + QString title = m_lectureTitle; + if (title.isEmpty()) + title = m_lectureURL.fileName(); + QDomElement titleElement = doc.createElement("Title"); + QDomText titleText = doc.createTextNode(title); + titleElement.appendChild(titleText); + lecture.appendChild(titleElement); + // store level stats + QDomElement levelStatsElement = doc.createElement("AllLevelStats"); + lecture.appendChild(levelStatsElement); + for (QValueVector<KTouchLevelStats>::ConstIterator it = m_levelStats.begin(); + it != m_levelStats.end(); ++it) + { + it->write(doc, levelStatsElement); + } + // store session stats + QDomElement sessionStatsElement = doc.createElement("AllSessionStats"); + lecture.appendChild(sessionStatsElement); + for (QValueVector<KTouchSessionStats>::ConstIterator it = m_sessionStats.begin(); + it != m_sessionStats.end(); ++it) + { + it->write(doc, sessionStatsElement); + } +} +// ---------------------------------------------------------------------------- + + + +// *** KTouchStatisticsData *** + +void KTouchStatisticsData::clear() { + m_lectureStats.clear(); +} +// ---------------------------------------------------------------------------- + +bool KTouchStatisticsData::read(QWidget * window, const KURL& url) { + if (url.isEmpty()) return false; + // Ok, first download the contents as usual using the KIO lib + // File is only downloaded if not local, otherwise it's just opened + QString target; + bool result = false; + if (KIO::NetAccess::download(url, target, window)) { + // Ok, that was successful, store the lectureURL and read the file + QFile infile(target); + if ( !infile.open( IO_ReadOnly ) ) { + KIO::NetAccess::removeTempFile(target); + return false; // Bugger it... couldn't open it... + } + QDomDocument doc; + +#ifdef COMPRESSED_XML_STATISTICS + QByteArray array; + array = infile.readAll(); + array = qUncompress(array); + doc.setContent( array ); +#else + doc.setContent( &infile ); +#endif // COMPRESSED_XML_STATISTICS + result = readStats(doc); + } + KIO::NetAccess::removeTempFile(target); + return result; +} +// ---------------------------------------------------------------------------- + +bool KTouchStatisticsData::write(QWidget * window, const KURL& url) const { + // create the XML document + QDomDocument doc; + writeStats(doc); + + // and save it + QString tmpFile; + KTempFile *temp=0; + if (url.isLocalFile()) + tmpFile=url.path(); // for local files the path is sufficient + else { + temp=new KTempFile; // for remote files create a temporary file first + temp->setAutoDelete(true); // so we don't have to delete the file ourselves + tmpFile=temp->name(); + } + + QFile outfile(tmpFile); + if ( !outfile.open( IO_WriteOnly ) ) { + if (temp) delete temp; + // kdDebug() << "Error creating lecture file!" << endl; + return false; + } + +#ifdef COMPRESSED_XML_STATISTICS + QByteArray array; + QTextStream out(array, IO_WriteOnly); + out << doc.toString(); + array = qCompress(array); + outfile.writeBlock(array); +#else + QTextStream out( &outfile ); + out << doc.toString(); +#endif // COMPRESSED_XML_STATISTICS + outfile.close(); + + // if we have a temporary file, we still need to upload it + if (temp) { + KIO::NetAccess::upload(tmpFile, url, window); + delete temp; + } + return true; +} +// ---------------------------------------------------------------------------- + +bool KTouchStatisticsData::readStats(QDomDocument& doc) { + clear(); // clear current data + // retrieve the KTouchStatistics statistics + QDomNodeList entries = doc.elementsByTagName("LectureStats"); + bool result = true; + unsigned int i=0; + while (result && i < entries.count()) { + KTouchLectureStats stats; + result = stats.read(entries.item(i)); + // do we have a valid URL? + if (stats.m_lectureURL.isEmpty()) result = false; + if (result) + m_lectureStats[stats.m_lectureURL] = stats; + ++i; + } + return result; +} +// ---------------------------------------------------------------------------- + +void KTouchStatisticsData::writeStats(QDomDocument& doc) const { + QDomElement root = doc.createElement( "KTouchStatistics" ); + doc.appendChild(root); + // Store Lecture statistics + LectureStatsMap::ConstIterator it = m_lectureStats.begin(); + while (it != m_lectureStats.end()) { + it.data().write(doc, root); + ++it; + } +} +// ---------------------------------------------------------------------------- + diff --git a/ktouch/src/ktouchstatisticsdata.h b/ktouch/src/ktouchstatisticsdata.h new file mode 100644 index 00000000..9d3dae0b --- /dev/null +++ b/ktouch/src/ktouchstatisticsdata.h @@ -0,0 +1,214 @@ +/*************************************************************************** + * ktouchstatisticsdata.h * + * ---------------------- * + * Copyright (C) 2005 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. * + ***************************************************************************/ + +#ifndef KTOUCHSTATISTICSDATA_H +#define KTOUCHSTATISTICSDATA_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qstring.h> +#include <qvaluevector.h> +#include <qmap.h> +#include <qdom.h> +#include <qdatetime.h> + +#include <kurl.h> + +#include <set> // I'm using std::set here because QT is missing this container type + +class QWidget; + + +// This file contains all class declarations dealing with the statistics obtained in KTouch. + + +// *** KTouchCharStats *** + +/// Contains the stats for a single character. +/// +/// Basically in this class the number of times the character has been pressed is counted. Both +/// the number of times it was correctly pressed and the number of times it was missed are stored +/// which allows to calculate a relative "hit-miss-ratio". +/// Character statistics are created for every character that has been missed at some time in the +/// training session. +class KTouchCharStats { + public: + /// Default constructor. + KTouchCharStats() : m_char(0), m_correctCount(0), m_wrongCount(0) {} + /// Constructor with parameters. + KTouchCharStats(QChar ch, unsigned int correct, unsigned int wrong) + : m_char(ch), m_correctCount(correct), m_wrongCount(wrong) {} + /// Reads the character statistics from a XML Dom Node. + /// @return Returns 'true', when reading was successful or 'false' otherwise. + bool read(QDomNode in); + /// Writes the character statistics to the XML document. + void write(QDomDocument& doc, QDomElement& root) const; + /// Returns the miss-hit ratio (a value between 0-all correct and 100-all wrong). + int missHitRatio() const; + + QChar m_char; ///< The character for which statistics are kept. + unsigned int m_correctCount; ///< How often the character has been typed correctly. + unsigned int m_wrongCount; ///< How often the character has been missed (not typed when it ought to be typed). +}; + +/// Sort criterion: Returns 'true' when the hit-miss ratio of 'lhs' is worse then the one of 'rhs'. +inline bool higher_miss_hit_ratio(const KTouchCharStats & lhs, const KTouchCharStats & rhs) { + return lhs.missHitRatio() > rhs.missHitRatio(); +} +/// Comparison operator "less", returns 'true' when the char-code of 'lhs' is less then the one of 'rhs' +inline bool operator<(const KTouchCharStats &lhs, const KTouchCharStats &rhs) { return lhs.m_char<rhs.m_char; } +/// Comparison operator "greater", returns 'true' when the char-code of 'lhs' is greater then the one of 'rhs' +inline bool operator>(const KTouchCharStats &lhs, const KTouchCharStats &rhs) { return lhs.m_char>rhs.m_char; } +/// Comparison operator == : returns 'true' when the char-code of 'lhs' is equal to then the one of 'rhs' +inline bool operator==(const KTouchCharStats &lhs, const KTouchCharStats &rhs) { return lhs.m_char==rhs.m_char; } +/// Writes the content of a KTouchCharStats object into the text stream. +QTextStream& operator<<(QTextStream &out, const KTouchCharStats &ch); + +// *** KTouchLevelStats *** + +/// Contains the complete statistics obtained for a single level. +/// +/// This class contains the statistics for a single run through a single level. It contains the +/// statistics for all characters that have been mistyped in this level, the typing time, the word count, +/// correct and total character count etc. +class KTouchLevelStats { + public: + /// Default constructor + KTouchLevelStats() { clear(); } + /// Clears the data + void clear(); + + /// Reads the level statistics from a XML Dom Node. + /// @return Returns 'true', when reading was successful or 'false' otherwise. + bool read(QDomNode in); + /// Writes the level statistics to the XML document. + void write(QDomDocument& doc, QDomElement& root) const; + /// Adds a correct character count to the current statistics + void addCorrectChar(QChar key); + /// Removes correct character count + void removeCorrectChar(); + /// Adds a wrong character count to the current statistics + void addWrongChar(QChar key); + /// Returns the typing accuracy or "correctness" as value between 0 and 1. + double correctness() const; + /// Returns the average typing speed in chars per minute. + double charSpeed() const; + /// Returns the average typing speed in words per minute. + double wordSpeed() const; + + std::set<KTouchCharStats> m_charStats; ///< Holds the statistics for mistyped characters. + int m_levelNum; ///< Number of the level in the lecture. + double m_elapsedTime; ///< Typing time in this level in seconds. + unsigned int m_words; ///< The number of typed words. + unsigned int m_totalChars; ///< The total number of chars typed this level. + unsigned int m_correctChars; ///< The total number of correctly typed chars. + QDateTime m_timeRecorded; ///< The time at which the level stats were recorded. +}; + + + +// *** KTouchSessionStats *** + +/// Contains the complete statistics obtained for a single training session (spanning several levels). +/// +/// This class contains the statistics for a complete training session of a single lecture but may include several levels. +class KTouchSessionStats { + public: + /// Default constructor + KTouchSessionStats() { clear(); } + /// Clears the data + void clear(); + + /// Reads the session statistics from a XML Dom Node. + /// @return Returns 'true', when reading was successful or 'false' otherwise. + bool read(QDomNode in); + /// Writes the session statistics to the XML document. + void write(QDomDocument& doc, QDomElement& root) const; + /// Adds a correct character count to the current statistics + void addCorrectChar(QChar key); + /// Removes a correct character count + void removeCorrectChar(); + /// Adds a wrong character count to the current statistics + void addWrongChar(QChar key); + + std::set<unsigned int> m_levelNums; ///< Numbers of the levels in this session. + std::set<KTouchCharStats> m_charStats; ///< Holds the statistics for mistyped characters. + double m_elapsedTime; ///< Typing time in this session in seconds. + unsigned int m_words; ///< The number of typed words. + unsigned int m_totalChars; ///< The total number of chars typed this session. + unsigned int m_correctChars; ///< The total number of correctly typed chars. + QDateTime m_timeRecorded; ///< The time at which the session stats were recorded. +}; + + +// *** KTouchLectureStats *** + +/// Contains the complete statistics obtained over time for a certain training lecture. +/// +/// This class contains the statistics for a complete training lecture including separate level statistics as well +/// as session statistics. +class KTouchLectureStats { + public: + /// Default constructor + KTouchLectureStats() {} + /// Clears the data + void clear(); + + /// Reads a lecture statistics from a XML Dom Node. + /// @return Returns 'true', when reading was successful or 'false' otherwise. + bool read(QDomNode in); + /// Writes the lecture statistics to the XML document. + void write(QDomDocument& doc, QDomElement& root) const; + + QString m_lectureTitle; ///< The descriptive title of the lecture. + KURL m_lectureURL; ///< The descriptive title of the lecture. + + QValueVector<KTouchLevelStats> m_levelStats; ///< The statistics for all levels in this lecture. + QValueVector<KTouchSessionStats> m_sessionStats; ///< The statistics for all session for in this lecture. +}; + + +// *** KTouchStatisticsData *** + +/// Contains the complete statistics for a certain user of KTouch. +/// +/// All user statistics is kept in this class. The statistics data is read when KTouch is started. New +/// statistics data is added when the lecture is changed or when the programm is closed. During +/// normal training sessions the statistics remains unchanged. During training the current level and +/// session statistics is obtained by and stored in the KTouchTrainer object. +class KTouchStatisticsData { + public: + typedef QMap<KURL, KTouchLectureStats> LectureStatsMap; + + /// Default constructor + KTouchStatisticsData() {} + /// Clears the data + void clear(); + + /// Reads the complete statistics from a XML file + /// @return Returns 'true', when reading was successful or 'false' otherwise. + bool read(QWidget * window, const KURL& url); + /// Writes the statistics data to the XML document. + bool write(QWidget * window, const KURL& url) const; + + LectureStatsMap m_lectureStats; ///< All statistics of all lectures, stored based on their URLs. + + private: + /// Reads the statistics data from file into an XML document + bool readStats(QDomDocument& doc); + /// Saves the statistics data in the XML document + void writeStats(QDomDocument& doc) const; +}; + +#endif // KTOUCHSTATISTICSDATA_H diff --git a/ktouch/src/ktouchstatus.cpp b/ktouch/src/ktouchstatus.cpp new file mode 100644 index 00000000..2c0d8341 --- /dev/null +++ b/ktouch/src/ktouchstatus.cpp @@ -0,0 +1,47 @@ +/*************************************************************************** + * ktouchstatus.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 "ktouchstatus.h" +#include "ktouchstatus.moc" + +#include <qlcdnumber.h> +#include <qlabel.h> +#include <qprogressbar.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include "prefs.h" + +KTouchStatus::KTouchStatus(QWidget *parent) + : KTouchStatusLayout(parent) +{ + applyPreferences(); +} + +void KTouchStatus::applyPreferences() { + if (Prefs::autoLevelChange()) { + levelUpBtn->setEnabled( !Prefs::disableManualLevelChange() ); + levelDownBtn->setEnabled( !Prefs::disableManualLevelChange() ); + } + else { + levelUpBtn->setEnabled( true ); + levelDownBtn->setEnabled( true ); + } +} + +void KTouchStatus::setNewChars(const QString& newChars) { + newCharsLabel->setText(newChars); +} + +void KTouchStatus::updateStatus(unsigned int level, double correctness) { + levelLCD->display(static_cast<int>(level)+1); // +1 because our level variable ranges from 0...n-1 + correctnessBar->setProgress(static_cast<int>(correctness*100) ); +} diff --git a/ktouch/src/ktouchstatus.h b/ktouch/src/ktouchstatus.h new file mode 100644 index 00000000..1782d3aa --- /dev/null +++ b/ktouch/src/ktouchstatus.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * ktouchstatus.h * + * -------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHSTATUS_H +#define KTOUCHSTATUS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ktouchstatuslayout.h" + +/** This is the implementation of the status widget (at the top of the main view). + * It simply displays the status but does not have a functionality of its own. + * Everythings is calculated in the trainer (see KTouchTrainer)and then displayed + * using the updateStatus() member function. + * The "new characters" string is set using the member function setNewChars(). + */ +class KTouchStatus : public KTouchStatusLayout { + Q_OBJECT + public: + /// Constructor + KTouchStatus(QWidget *parent); + /// Called when the preferences have changed in the configuration dialog. + void applyPreferences(); + /// Sets the new characters text. + void setNewChars(const QString& newChars); + /// Updates the level LCD and the correctness progress bar + void updateStatus(unsigned int level, double correctness); +}; + +#endif // KTOUCHSTATUS_H diff --git a/ktouch/src/ktouchstatuslayout.ui b/ktouch/src/ktouchstatuslayout.ui new file mode 100644 index 00000000..80b29f9f --- /dev/null +++ b/ktouch/src/ktouchstatuslayout.ui @@ -0,0 +1,360 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KTouchStatusLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KTouchStatusLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>661</width> + <height>100</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox1</cstring> + </property> + <property name="title"> + <string>Level</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>2</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLCDNumber"> + <property name="name"> + <cstring>levelLCD</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>30</height> + </size> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="numDigits"> + <number>3</number> + </property> + <property name="mode"> + <enum>Dec</enum> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Current level</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This shows which level you are at.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>levelUpBtn</cstring> + </property> + <property name="maximumSize"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + <property name="text"> + <string>+</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>levelDownBtn</cstring> + </property> + <property name="maximumSize"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + <property name="text"> + <string>-</string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox11</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Speed</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + <widget class="QLCDNumber"> + <property name="name"> + <cstring>speedLCD</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>30</height> + </size> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="numDigits"> + <number>3</number> + </property> + <property name="segmentStyle"> + <enum>Flat</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Characters/Minute</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This shows how fast you are typing. It measures the typing speed in characters per minute.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox12</cstring> + </property> + <property name="title"> + <string>Correctness</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + <widget class="QProgressBar"> + <property name="name"> + <cstring>correctnessBar</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>How much are you typing correctly?</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This shows how correctly you are typing.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox4</cstring> + </property> + <property name="title"> + <string>New Characters in This Level</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>newCharsLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>20</pointsize> + </font> + </property> + <property name="text"> + <string>new characters</string> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + <property name="hAlign" stdset="0"> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> +</widget> +<slots> + <slot>levelForcedDown()</slot> + <slot>levelForcedUp()</slot> + <slot>showStatChanged(int)</slot> + <slot>runningStateChanged(int)</slot> + <slot>startKTouch()</slot> + <slot>stopKTouch()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/ktouch/src/ktouchtrainer.cpp b/ktouch/src/ktouchtrainer.cpp new file mode 100644 index 00000000..81c9da9e --- /dev/null +++ b/ktouch/src/ktouchtrainer.cpp @@ -0,0 +1,502 @@ +/*************************************************************************** + * ktouchtrainer.cpp * + * ----------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2006 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 "ktouchtrainer.h" +#include "ktouchtrainer.moc" + +#include <qlcdnumber.h> +#include <qfile.h> + +#include <kdebug.h> +#include <kpushbutton.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kaudioplayer.h> +#include <qmessagebox.h> + +#include "ktouch.h" +#include "ktouchstatus.h" +#include "ktouchslideline.h" +#include "ktouchkeyboardwidget.h" +#include "ktouchlecture.h" +#include "ktouchdefaults.h" +#include "prefs.h" + +KTouchTrainer::KTouchTrainer(KTouchStatus *status, KTouchSlideLine *slideLine, KTouchKeyboardWidget *keyboard, KTouchLecture *lecture) + : QObject(), + m_trainingTimer(new QTimer), + m_statusWidget(status), + m_slideLineWidget(slideLine), + m_keyboardWidget(keyboard), + m_lecture(lecture) +{ + m_level = 0; // level 1, but we're storing it zero based + m_line = 0; + m_wordsInCurrentLine = 0; + m_trainingPaused=true; // we start in pause mode + m_teacherText=m_lecture->level(0).line(0); + m_studentText=""; + + // reset statistics + m_levelStats.clear(); + m_sessionStats.clear(); + + /// \todo preload sounds and improve sound playback system + m_levelUpSound = KGlobal::dirs()->findResource("appdata","up.wav"); + m_levelDownSound = KGlobal::dirs()->findResource("appdata","down.wav"); + m_typeWriterSound = KGlobal::dirs()->findResource("appdata","typewriter.wav"); + + connect(m_statusWidget->levelUpBtn, SIGNAL(clicked()), this, SLOT(levelUp()) ); + connect(m_statusWidget->levelDownBtn, SIGNAL(clicked()), this, SLOT(levelDown()) ); + connect(m_trainingTimer, SIGNAL(timeout()), this, SLOT(timerTick()) ); +} +// ---------------------------------------------------------------------------- + +KTouchTrainer::~KTouchTrainer() { + delete m_trainingTimer; +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::gotoFirstLine() { + m_statusWidget->setNewChars( m_lecture->level(m_level).newChars() ); + m_line=0; + newLine(); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::keyPressed(QChar key) { + // NOTE : In this function we need to distinguish between left and right + // typing. Use the config setting Prefs::right2LeftTyping() for that. + + if (m_trainingPaused) continueTraining(); + if (m_teacherText==m_studentText) { + // if already at end of line, don't add more chars + /// \todo Flash the line when line complete + if (Prefs::beepOnError()) QApplication::beep(); + return; + } + // remember length of student text without added character + unsigned int old_student_text_len = m_studentText.length(); + // compose new student text depending in typing direction + QString new_student_text = m_studentText; + if (Prefs::right2LeftTyping()) + new_student_text = key + new_student_text; + else + new_student_text += key; + + // don´t allow excessive amounts of characters per line + if (!m_slideLineWidget->canAddCharacter(new_student_text)) { + if (Prefs::beepOnError()) QApplication::beep(); + return; + } + // store the new student text + m_studentText = new_student_text; + // we need to find out, if the key was correct or not + if (studentLineCorrect()) + statsAddCorrectChar(key); // ok, all student text is correct + else { + // nope, the key was wrong : beep !!! + if (Prefs::beepOnError()) QApplication::beep(); + // check if the key is the first wrong key that was mistyped. Only then add it + // to the wrong char statistics. + if (Prefs::right2LeftTyping()) { + if (m_teacherText.right(old_student_text_len)==m_studentText.right(old_student_text_len) && + m_teacherText.length() > old_student_text_len) + { + // add the key the student ought to press to the wrong character stats + int next_key_index = m_teacherText.length() - old_student_text_len; +// kdDebug() << "Wrong key = " << m_teacherText[next_key_index] << endl; + statsAddWrongChar( m_teacherText[next_key_index] ); + } + } + else { + if (m_teacherText.left(old_student_text_len)==m_studentText.left(old_student_text_len) && + m_teacherText.length() > old_student_text_len) + { + // add the key the student ought to press to the wrong character stats + int next_key_index = old_student_text_len; + statsAddWrongChar( m_teacherText[next_key_index] ); + } + } + /// \todo Implement option whether subsequent mistyped keys should be remembered as missed + /// keys as well. + } + updateWidgets(); // update all the other widgets (keyboard widget, status widget and statusbar) +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::backspacePressed() { + if (m_trainingPaused) continueTraining(); + /// \todo Implement the "remove space character = remove word count" feature + + unsigned int len = m_studentText.length(); + if (len) { + if (m_teacherText.left(len)==m_studentText && m_teacherText.length()>=len) { + // we are removing a correctly typed char + statsRemoveCorrectChar(m_studentText[len-1]); + } + m_studentText = m_studentText.left(--len); + updateWidgets(); // update all the widgets and the word count + if (m_teacherText.left(len)==m_studentText) + m_keyboardWidget->newKey(m_teacherText[len]); + else + m_keyboardWidget->newKey(QChar(8)); + } + else { + /// \todo Flash line when Backspace on empty line + QApplication::beep(); + } +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::enterPressed() { + if (m_trainingPaused) continueTraining(); + if (m_studentText!=m_teacherText) { + QApplication::beep(); + return; + }; + + /* + // NOTE : auto level change inside level was removed due to popular request + + if (Prefs::autoLevelChange()) { + // if level increase criterion was fulfilled, increase line counter + if (Prefs::upCorrectLimit() <= m_session.correctness()*100 && + Prefs::upSpeedLimit() <= m_session.charSpeed()) + { + m_decLinesCount=0; + ++m_incLinesCount; + } + else if (Prefs::downCorrectLimit() > m_session.correctness()*100 || + Prefs::downSpeedLimit() > m_session.charSpeed()) + { + m_incLinesCount=0; + ++m_decLinesCount; + }; + // Automatic level change after a number of lines can happen, if you fulfilled the + // requirements in the last 5 lines. + if (m_incLinesCount>=2) { + levelUp(); + return; + } + if (m_decLinesCount>=2 && m_level!=0) { + levelDown(); + return; + }; + }; + */ + + + // Check if we are in the last line + if (m_line+1 >= m_lecture->level(m_level).count()) { + if (Prefs::autoLevelChange()) { + // adjust level if limits exceeded + if (Prefs::upCorrectLimit() <= m_levelStats.correctness()*100 && + Prefs::upSpeedLimit() <= m_levelStats.charSpeed()) + { + /// \todo Test if last level is done and show message, stop training, show statistics etc. + levelUp(); // level change takes care of updating word count + return; + } + else if (Prefs::downCorrectLimit() > m_levelStats.correctness()*100 || + Prefs::downSpeedLimit() > m_levelStats.charSpeed()) + { + levelDown(); // level change takes care of updating word count + return; + } + } + // we have to store the word count before continuing in the first line + updateWordCount(); + m_levelStats.m_words += m_wordsInCurrentLine; + m_sessionStats.m_words += m_wordsInCurrentLine; + gotoFirstLine(); // restart in the new/current level + } + else { + // store the word count + updateWordCount(); + m_levelStats.m_words += m_wordsInCurrentLine; + m_sessionStats.m_words += m_wordsInCurrentLine; + ++m_line; + newLine(); // otherwise show next line + } +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::updateWidgets() { + // update status widget + m_statusWidget->updateStatus(m_level, m_levelStats.correctness()); + // update slide line widget + m_slideLineWidget->setStudentText(m_studentText); + // update keyboard widget -> show next to be pressed char. + // we have to find out first whether the student text is correct or not. + if (studentLineCorrect()) { + // ok, all student text is correct + if (m_teacherText.length()==m_studentText.length()) + m_keyboardWidget->newKey(QChar(13)); // we have reached the end of the line + else + m_keyboardWidget->newKey(m_teacherText[m_studentText.length()]); + } + else { + m_keyboardWidget->newKey(QChar(8)); // wrong key, user must now press backspace + } + updateWordCount(); // here we first update the word count + updateStatusBar(); // and then the status bar +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::startTraining(bool keepLevel) { + // Here we start a new training session. + + // keep the current level if flag is set + if (!keepLevel) + m_level=0; + // reset the level and session statistics + m_levelStats.clear(); + m_sessionStats.clear(); + // transfer level to level statistics + m_levelStats.m_levelNum = m_level; + // remember level in session stats + m_sessionStats.m_levelNums.insert(m_level); + // go to first line in level (also resets word count) + gotoFirstLine(); + updateStatusBarMessage(i18n("Starting training session: Waiting for first keypress...") ); + updateStatusBar(); + m_statusWidget->updateStatus(m_level, 1); + m_statusWidget->speedLCD->display( 0 ); + m_trainingPaused=true; // Go into "Pause" mode + m_trainingTimer->stop(); // Training timer will be started on first keypress. + m_slideLineWidget->setCursorTimerEnabled(true); // Curser will blink + updateLevelChangeButtons(); +} +// ---------------------------------------------------------------------------- + +// Pauses the current training session. +// This function is called from class KTouch (when the Pause action is executed). +void KTouchTrainer::pauseTraining() { + m_trainingPaused=true; + m_trainingTimer->stop(); + m_slideLineWidget->setCursorTimerEnabled(false); + m_statusWidget->updateStatus(m_level, m_levelStats.correctness()); + m_statusWidget->speedLCD->display( m_levelStats.charSpeed() ); + updateStatusBarMessage(i18n("Training session paused. Training continues on next keypress...") ); + updateStatusBar(); +} +// ---------------------------------------------------------------------------- + +// Continues the current training session. +// This function is called from class KTouch when a user presses a normal key +// while the training is in pause mode. +void KTouchTrainer::continueTraining() { + m_trainingPaused=false; + m_slideLineWidget->setCursorTimerEnabled(true); + m_statusWidget->updateStatus(m_level, m_levelStats.correctness() ); + m_statusWidget->speedLCD->display( m_levelStats.charSpeed() ); + updateStatusBarMessage(i18n("Training session! The time is running...") ); + updateStatusBar(); + m_trainingTimer->start(LCD_UPDATE_INTERVAL); // start the timer +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::storeTrainingStatistics() { + // at first get a reference to the statistics data of the current lecture + KTouchLectureStats& data = KTouchPtr->getCurrentLectureStats(); + // update word count + updateWordCount(); + // add word count to level and session statistics + m_levelStats.m_words += m_wordsInCurrentLine; + m_sessionStats.m_words += m_wordsInCurrentLine; + // are there level stats to be stored? + if (m_levelStats.m_elapsedTime != 0) { + //kdDebug() << "[KTouchTrainer::storeTrainingStatistics] Storing level statistics!" << endl; + m_levelStats.m_timeRecorded = QDateTime::currentDateTime(); + data.m_levelStats.push_back( m_levelStats ); + } + if (m_sessionStats.m_elapsedTime != 0) { + //kdDebug() << "[KTouchTrainer::storeTrainingStatistics] Storing session statistics!" << endl; + m_sessionStats.m_timeRecorded = QDateTime::currentDateTime(); + data.m_sessionStats.push_back( m_sessionStats ); + } +} +// ---------------------------------------------------------------------------- + +bool KTouchTrainer::studentLineCorrect() const { + if (m_teacherText.isEmpty()) + return m_studentText.isEmpty(); + unsigned int len = m_studentText.length(); + // different check for left and right writing + if (Prefs::right2LeftTyping()) + return m_teacherText.right(len)==m_studentText && m_teacherText.length()>=len; + else + return (m_teacherText.left(len)==m_studentText && m_teacherText.length()>=len); +} +// ---------------------------------------------------------------------------- + + +// *** Public slots *** + +void KTouchTrainer::levelUp() { + KAudioPlayer::play(m_levelUpSound.url()); + ++m_level; // increase the level + if (m_level>=m_lecture->levelCount()) { + // already at max level? Let's stay there + m_level=m_lecture->levelCount()-1; + levelAllComplete(); + /// \todo Do something when last level is completed + } + // Store level statistics if level is increased + statsChangeLevel(); + gotoFirstLine(); + updateLevelChangeButtons(); +} + + +// ---------------------------------------------------------------------------- + +void KTouchTrainer::levelDown() { + if (m_level>0) { + --m_level; + KAudioPlayer::play(m_levelDownSound.url()); + } + // Store level statistics if level is increased + statsChangeLevel(); + gotoFirstLine(); + updateLevelChangeButtons(); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::timerTick() { + if (m_trainingPaused) return; + // Add the timer interval. I think we can neglect the error we make when the session is + // paused and continued... it's not a scientific calculation, isn't it? + statsAddTime(LCD_UPDATE_INTERVAL*0.001); + // update only the widgets that are affected by progressing time + m_statusWidget->speedLCD->display( m_levelStats.charSpeed() ); +} +// ---------------------------------------------------------------------------- + + + +// *** Private functions *** + +void KTouchTrainer::levelAllComplete() { + QMessageBox::information(0, i18n("You rock!"), + i18n("You have finished this training exercise.\n" + "This training session will start from the beginning.")); + statsChangeLevel(); + startTraining(false); +} + +void KTouchTrainer::updateLevelChangeButtons() { + if (!Prefs::disableManualLevelChange()) { + m_statusWidget->levelUpBtn->setEnabled(m_level < m_lecture->levelCount() - 1); + m_statusWidget->levelDownBtn->setEnabled(m_level > 0); + } +} +void KTouchTrainer::newLine() { + m_teacherText = m_lecture->level(m_level).line(m_line); + m_studentText=""; + m_wordsInCurrentLine = 0; + m_keyboardWidget->newKey(m_teacherText[0]); + m_slideLineWidget->setNewText(m_teacherText, m_studentText); + updateStatusBar(); // update status bar +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::updateStatusBar() const { + KTouchPtr->changeStatusbarStats(m_levelStats.m_correctChars, m_levelStats.m_totalChars, + m_levelStats.m_words + m_wordsInCurrentLine, + m_sessionStats.m_correctChars, m_sessionStats.m_totalChars, + m_sessionStats.m_words + m_wordsInCurrentLine); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::updateStatusBarMessage(const QString& message) const { + KTouchPtr->changeStatusbarMessage(message); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::updateWordCount() { + // now update the m_wordsInCurrentLine variable + if (!studentLineCorrect()) return; // if error, don't update + int words = 0; + bool space = true; + for (unsigned int i=0; i<m_studentText.length(); ++i) { + bool is_space = (m_studentText[i] == QChar(' ')); + if (is_space) { + if (space) continue; // two spaces after each other... ignore + else { + ++words; + space = true; + } + } + else { + if (!space) continue; // two chars after each other... ignore + else { + space = false; // no need to add a word here. + } + } + } + // check if line is completely typed and add a word then + if (m_studentText == m_teacherText) ++words; + m_wordsInCurrentLine = words; +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::statsAddCorrectChar(QChar key) { + m_levelStats.addCorrectChar(key); + m_sessionStats.addCorrectChar(key); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::statsAddWrongChar(QChar key) { + m_levelStats.addWrongChar(key); + m_sessionStats.addWrongChar(key); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::statsRemoveCorrectChar(QChar) { + m_levelStats.removeCorrectChar(); + m_sessionStats.removeCorrectChar(); +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::statsAddTime(double dt) { + m_levelStats.m_elapsedTime += dt; + m_sessionStats.m_elapsedTime += dt; +} +// ---------------------------------------------------------------------------- + +void KTouchTrainer::statsChangeLevel() { + //kdDebug() << "[KTouchTrainer::statsChangeLevel] First!" << endl; + // first update word count and store data in + updateWordCount(); + //kdDebug() << "[KTouchTrainer::statsChangeLevel] Adding word count of " << m_wordsInCurrentLine << endl; + m_levelStats.m_words += m_wordsInCurrentLine; + m_sessionStats.m_words += m_wordsInCurrentLine; + // get a reference to the statistics data of the current lecture + KTouchLectureStats& data = KTouchPtr->getCurrentLectureStats(); + // are there level stats to be stored? + if (m_levelStats.m_elapsedTime != 0) { + //kdDebug() << "[KTouchTrainer::storeTrainingStatistics] Storing level statistics!" << endl; + m_levelStats.m_timeRecorded = QDateTime::currentDateTime(); + data.m_levelStats.push_back( m_levelStats ); + } + // clear level stats + m_levelStats.clear(); + // transfer current level to level statistics + m_levelStats.m_levelNum = m_level; + // remember level in session stats + m_sessionStats.m_levelNums.insert(m_level); + // show new level (in status widet) and 100% correctness + m_statusWidget->updateStatus(m_level, 1); +} +// ---------------------------------------------------------------------------- diff --git a/ktouch/src/ktouchtrainer.h b/ktouch/src/ktouchtrainer.h new file mode 100644 index 00000000..46c9d02c --- /dev/null +++ b/ktouch/src/ktouchtrainer.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * ktouchtrainer.h * + * --------------- * + * Copyright (C) 2000 by Håvard Frøiland, 2003 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. * + ***************************************************************************/ + +#ifndef KTOUCHTRAINER_H +#define KTOUCHTRAINER_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qobject.h> + +#include "ktouchstatisticsdata.h" + +class QTimer; + +class KTouchStatus; +class KTouchSlideLine; +class KTouchKeyboardWidget; +class KTouchLecture; + +/// This is the master trainer object and does all the heavy work. +/// +/// The training object gets the already filtered "new key pressed" events from the main window +/// and processes them. It manages the level and line adjustments, starts, pauses and restarts +/// a training, updates the widgets and and and... +/// <p> +/// Whether in training or not the user may switch levels using the level change buttons (if enabled) +/// or change the lecture. The slot levelUp() or levelDown() is called and the trainer is switching +/// levels accordingly. +/// <p> +/// There is always a current training session. On a typical program start the training session is paused. +/// When a training session is paused the timer is stopped and the program waits for the next keypress +/// of the user and then the timer is restarted. Note that the Pause button (Pause action) is enabled/disabled from +/// the main KTouch class. +/// <p> +/// When the user starts a new training session or quits the program the current level/session +/// statistics data will be added to the user statistics and the stats will be cleared. If a new +/// level is started (during a session), only the level stats are stored and cleared, the session +/// statistics is kept. The only member functions, that add statistics data to the users statistics are +/// therefore storeTrainingStatistics() (for level and session stats) and statsChangeLevel() (stores only +/// level stats). +/// <p> +/// The trainer keeps record of the current level and session stats seperately and +/// stores them in the m_levelStats and m_sessionStats member variables. However, the word count is only +/// updated in these variables, once a slide line is completed (or a level/session/lecture is changed). +/// <p> +/// The member function updateWidgets() updates all training related widgets (status widget, slide line, +/// and the statusbar of the main window). In this member function also the word count of the current line +/// is updated. +class KTouchTrainer : public QObject { + Q_OBJECT + public: + /// Constructor. + KTouchTrainer(KTouchStatus *status, KTouchSlideLine *slideLine, KTouchKeyboardWidget *keyboard, KTouchLecture *lecture); + /// Destructor + virtual ~KTouchTrainer(); + /// Jumps to the first line in the current level (m_level) (Note: has no effect on the training statistics). + /// The function resets the word count. + void gotoFirstLine(); + /// Jumps to the first level, line 1 (Note: has no effect on the training statistics!). + /// The function resets the word count. + void gotoFirstLevel() { m_level=0; gotoFirstLine(); } + /// Processes the character 'key' and updates the widgets and statistics. + /// If the training session was started and in waiting state the timer is started and the actuall training begins. + void keyPressed(QChar key); + /// Will be called when a character has been removed (recalculates the variables and updates the widgets). + void backspacePressed(); + /// Will be called when "enter" has been pressed (displays next line). + void enterPressed(); + /// Will be called whenever the student text has changed and updates the slide line widget, + /// the status widget and shows the next to be pressed key on the keyboard + void updateWidgets(); + /// Starts a new training session for the current lecture. + /// This function is called from class KTouch (when the "New" session action is executed). + /// The function resets the statistics so the caller must ensure that the previous level + /// and session statistics are properly stored. + void startTraining(bool keepLevel); + /// Pauses the current training session. + /// This function is called from class KTouch (when the "Pause" action is executed). + void pauseTraining(); + /// Continues the current training session. + /// This function is called from class KTouch when a user presses a normal key while the training is in + /// "Pause" mode. + void continueTraining(); + /// Stores the currently accumulated statistics in the statistics for the current user (but only, if + /// some time has elapsed in the current training session). This function is called whenever a new + /// training session is started or when the program is closed. + void storeTrainingStatistics(); + /// Returns true, if the students line is correct so far. + bool studentLineCorrect() const; + /// Returns number of words that have been typed so far in the current line. + unsigned int wordsInCurrentLine() const { return m_wordsInCurrentLine; } + + + unsigned int m_level; ///< Current level number (zero based). + unsigned int m_line; ///< Current line number (zero based). + + QString m_teacherText; ///< The currently displayed teacher text. + QString m_studentText; ///< The currently typed student text. + + bool m_trainingPaused; ///< Indicates whether we have paused the training session or not. + QTimer *m_trainingTimer; ///< The timer responsable for updating the speed LCD widget. + + KTouchLevelStats m_levelStats; ///< Contains the statistics for the current level alone. + KTouchSessionStats m_sessionStats; ///< Contains the statistics for the current session (multiple levels). + + public slots: + /// Will be called when the level is increased. + void levelUp(); + /// Will be called when the level is decreased. + void levelDown(); + + private slots: + /// Will be called regularly by the training timer, increases the elapsed time and updates the speed LCD widget. + void timerTick(); + + private: + /// Sets up a new teacher line (depending on m_level and m_line). + /// The function resets the word count. + void newLine(); + void levelAllComplete(); + void updateLevelChangeButtons(); + /// Updates the statusbar of the main window. + void updateStatusBar() const; + /// Updates the message in the statusbar of the main window. + void updateStatusBarMessage(const QString& message) const; + /// Updates the word count in the current student text line. + void updateWordCount(); + /// Adds a correct character count to the current statistics. + void statsAddCorrectChar(QChar key); + /// Adds a wrong character count to the current statistics. + void statsAddWrongChar(QChar key); + /// Removes a correctly typed character count. + void statsRemoveCorrectChar(QChar); + /// Adds some elapsed time (in seconds) to the statistics. + void statsAddTime(double dt); + /// Stores level stats and resets level stats (this function is called from levelUp() and levelDown() + /// right before a new level and new line is started). + void statsChangeLevel(); + + KTouchStatus *m_statusWidget; ///< Pointer to the status widget on top of the main window. + KTouchSlideLine *m_slideLineWidget; ///< Pointer to the sliding line widget. + KTouchKeyboardWidget *m_keyboardWidget; ///< Pointer to the keyboard widget. + KTouchLecture *m_lecture; ///< Pointer to the lecture data. + + KURL m_levelUpSound; ///< URL of the level up sound. + KURL m_levelDownSound; ///< URL of the level down sound. + KURL m_typeWriterSound; ///< URL of the typing sound. + + unsigned int m_wordsInCurrentLine; ///< Number of words in the current typing line. +}; + +#endif // KTOUCHTRAINER_H diff --git a/ktouch/src/ktouchui.rc b/ktouch/src/ktouchui.rc new file mode 100644 index 00000000..36db42f6 --- /dev/null +++ b/ktouch/src/ktouchui.rc @@ -0,0 +1,29 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="ktouch" version="1.5.3"> +<MenuBar> + <Menu name="file"><text>F&ile</text> + <Action name="file_openlecture" /> + <Action name="file_editlecture" /> + <Separator/> + <Action name="file_editcolors" /> + <Action name="file_editkeyboard" /> + </Menu> + <Menu name="training"><text>T&raining</text> + <Action name="training_newsession" /> + <Action name="training_pause" /> + <Separator/> + <Action name="training_stats" /> + <Action name="default_lectures" /> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Action name="keyboard_layouts" append="save_merge"/> + <Action name="keyboard_schemes" append="save_merge"/> + </Menu> +</MenuBar> +<ToolBar name="mainToolBar"><text>Main Toolbar</text> + <Action name="training_newsession" /> + <Action name="training_pause" /> + <Separator/> + <Action name="training_stats" /> +</ToolBar> +</kpartgui> diff --git a/ktouch/src/ktouchutils.cpp b/ktouch/src/ktouchutils.cpp new file mode 100644 index 00000000..af039f2c --- /dev/null +++ b/ktouch/src/ktouchutils.cpp @@ -0,0 +1,43 @@ +/*************************************************************************** + * ktouchutils.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 <qtextstream.h> +#include <qstringlist.h> +#include <qmap.h> + +#include <kdebug.h> + +#include "ktouchutils.h" + +QString format_speed(double speed) { + unsigned int mins = static_cast<unsigned int>(speed); + unsigned int secs = static_cast<unsigned int>(speed*60) - mins*60; + return QString("%1' %2''").arg(mins).arg(secs); +} + +void sort_lists(QStringList& text, QStringList& data) { + if (text.count() != data.count()) return; // invalid input, no sorting possible + QMap<QString, QString> sort_map; + QStringList::const_iterator key_it = text.constBegin(); + QStringList::const_iterator val_it = data.constBegin(); + while (key_it != text.constEnd()) { +// kdDebug() << *key_it << " " << *val_it << endl; + sort_map[*key_it++] = *val_it++; + } + text.sort(); + key_it = text.constBegin(); + data.clear(); + while (key_it != text.constEnd()) { +// kdDebug() << *key_it << " " << sort_map[*key_it] << endl; + data.append( sort_map[*key_it++] ); + } +} diff --git a/ktouch/src/ktouchutils.h b/ktouch/src/ktouchutils.h new file mode 100644 index 00000000..53b0daee --- /dev/null +++ b/ktouch/src/ktouchutils.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * ktouchutils.h * + * ------------- * + * 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. * + ***************************************************************************/ + +#ifndef KTOUCHUTILS_H +#define KTOUCHUTILS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qstring.h> + +/// Formats the speed (given in chars/min) and creates a nice looking text. +QString format_speed(double speed); + +/// Sorts both lists but uses only the values in "text" for sorting. +void sort_lists(QStringList& text, QStringList& data); + +#endif // KTOUCHUTILS_H diff --git a/ktouch/src/main.cpp b/ktouch/src/main.cpp new file mode 100644 index 00000000..a5159ad1 --- /dev/null +++ b/ktouch/src/main.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * 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 <kaboutdata.h> +#include <kcmdlineargs.h> + +#include <kdebug.h> + +static const char description[] = + I18N_NOOP("A program that helps you to learn and practice touch typing"); + +static const char version[] = "1.5.3"; + +static KCmdLineOptions options[] = { + { "+[URL]", I18N_NOOP( "Training file to open" ), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char **argv) +{ + KAboutData about("ktouch", + I18N_NOOP("KTouch"), + version, + description, + KAboutData::License_GPL, + "Copyright (C) 2000-2006 by Håvard Frøiland and Andreas Nicolai", + 0, + "http://edu.kde.org/ktouch", + "submit@bugs.kde.org"); + about.addAuthor( "Andreas Nicolai", I18N_NOOP("Current maintainer and programmer"), "Andreas.Nicolai@gmx.net" ); + about.addAuthor( "Håvard Frøiland", I18N_NOOP("Original author, project admin"), "haavard@users.sourceforge.net" ); + about.addCredit( "David Vignoni", I18N_NOOP("Creator of the SVG icon"), "david80v@tin.it"); + about.addCredit( "Anne-Marie Mahfouf", I18N_NOOP("Lots of patches, fixes, updates"), "annma@kde.org"); + about.addCredit( "All the creators of training and keyboard files", 0, 0); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + KTouch *mainWin = 0; + + if (app.isRestored()) { + RESTORE(KTouch); + } + else + { + // no session.. just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + // TODO: check the command line for a training file and set this as the new to open training file + + /* TODO: turn splash screen on in release + KTouchSplash splash; // create the splash screen + splash.setHideEnabled( true ); // allow user to click the splash screen away + splash.show(); // show it + QTimer splashTimer; + splashTimer.connect(&splashTimer, SIGNAL(timeout()), &splash, SLOT(hide())); + splashTimer.start(2000, true); // start singleshot timer to hide the splashscreen + */ + + mainWin = new KTouch(); + app.setMainWidget( mainWin ); + + mainWin->show(); + args->clear(); + } + + return app.exec(); +} diff --git a/ktouch/src/prefs.kcfgc b/ktouch/src/prefs.kcfgc new file mode 100644 index 00000000..d5d5d7ba --- /dev/null +++ b/ktouch/src/prefs.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=ktouch.kcfg +IncludeFiles=kglobalsettings.h +ClassName=Prefs +Singleton=true +Mutators=true |