1 /*
2 SPDX-FileCopyrightText: 2001-2008 Anne-Marie Mahfouf <annma@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "klettresview.h"
8
9 #include <QTimer>
10 #include <QPainter>
11 #include <QSvgRenderer>
12 #include <QFile>
13 #include <QPaintEvent>
14 #include <QStandardPaths>
15
16 #include <KLocalizedString>
17
18 //Project headers
19 #include "klettres.h"
20 #include "prefs.h"
21 #include "kltheme.h"
22 #include "langutils.h"
23 #include "klettres_debug.h"
24
KLettresView(KLettres * parent)25 KLettresView::KLettresView(KLettres *parent)
26 : QWidget(parent)
27 {
28 m_klettres = parent;
29
30 //lineEdit for user input
31 m_letterEdit = new QLineEdit( this );
32 m_letterEdit->setToolTip(i18n("Type the letter or syllable that you just heard" ) );
33 m_letterEdit->setFont(Prefs::font());
34 m_letterEdit->setContextMenuPolicy(Qt::NoContextMenu);
35 m_letterEdit->setAutoFillBackground(true);
36
37 randomInt = 0;
38 m_renderer = new QSvgRenderer();
39 setTheme(KLThemeFactory::instance()->buildTheme(0));
40 }
41
~KLettresView()42 KLettresView::~KLettresView()
43 {
44 delete m_renderer;
45 delete m_theme;
46 }
47
chooseSound()48 void KLettresView::chooseSound()
49 {
50 //get the next random sound
51 m_random=m_klettres->soundFactory->randomList[randomInt%m_klettres->soundFactory->sounds];
52 //The sound is played
53 qCDebug(KLETTRES_LOG) << "m_random " << m_random;
54 m_klettres->soundFactory->playSound(m_random);
55 //store letter or syllable in m_currentLetter
56 m_currentLetter = m_klettres->soundFactory->namesList[m_random];
57 //Find the length of the syllable
58 m_length=m_klettres->soundFactory->namesList[m_random].length();
59 qCDebug(KLETTRES_LOG) << "syllable length " << m_length;
60 int width;
61
62 if (m_length<3) {
63 width = 200;
64 } else {
65 width = 200+(20*(m_length-2));
66 }
67
68 m_letterEdit->setMinimumSize( QSize( width, 100 ) );
69 m_letterEdit->setMaximumSize( QSize( width, 100 ) );
70 update();
71 }
72
setTheme(KLTheme * theme)73 void KLettresView::setTheme(KLTheme *theme)
74 {
75 // we don't allow null themes
76 if (!theme)
77 return;
78
79 QString svgpath = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
80 QStringLiteral("klettres/pics/%1/%2").arg(theme->name(), theme->svgFileName()));
81
82 // we don't allow themes with no svg installed
83 if (!QFile::exists(svgpath)) {
84 return;
85 }
86 delete m_theme;
87 m_theme = theme;
88
89 // stylesheet
90 int r1, g1, b1;
91 m_theme->backgroundInputColor().getRgb(&r1, &g1, &b1);
92 int r2, g2, b2;
93 m_theme->letterInputColor().getRgb(&r2, &g2, &b2);
94 m_letterEdit->setStyleSheet(QStringLiteral("border-style: solid; background-color: rgb(%1, %2, %3); color: rgb(%4, %5, %6) ; border-color: rgb(%4, %5, %6); border-bottom-right-radius:10; border-radius: 15px; border-width: 3px").arg(r1).arg(g1).arg(b1).arg(r2).arg(g2).arg(b2));
95
96 m_renderer->load(svgpath);
97 m_backgroundCache = QPixmap();
98 update();
99 }
100
paintEvent(QPaintEvent * e)101 void KLettresView::paintEvent( QPaintEvent * e )
102 {
103 QPainter p(this);
104 p.setRenderHint(QPainter::SmoothPixmapTransform);
105 paintBackground(p, e->rect());
106 paintLetter(p, e->rect());
107 m_letterEdit->setFont(Prefs::font());
108 }
109
paintBackground(QPainter & p,const QRect & rect)110 void KLettresView::paintBackground(QPainter &p, const QRect& rect)
111 {
112 // Draw the background
113 if (m_backgroundCache.size() != size()) {
114 m_backgroundCache = QPixmap(size());
115 QPainter aux(&m_backgroundCache);
116 m_renderer->render(&aux);
117 }
118 p.drawPixmap(rect.topLeft(), m_backgroundCache, rect);
119 }
120
paintLetter(QPainter & p,const QRect & rect)121 void KLettresView::paintLetter(QPainter &p, const QRect& rect)
122 {
123 if (Prefs::level()%2==1) {
124 QRect myRect = m_theme->wordRect(size());
125 if (!myRect.intersects(rect)) {
126 return;
127 }
128
129 const QString letterInLower = m_currentLetter.toLower();
130 const QString prompt = (letterInLower == m_currentLetter) ? m_currentLetter
131 : i18nc("%1 is uppercase letter, %2 is the same in lowercase", "%1 / %2", m_currentLetter, letterInLower);
132 p.setPen( m_theme->letterColor());
133 p.setFont(Prefs::font());
134 p.drawText(myRect, prompt);
135 }
136 m_letterEdit->setGeometry( m_theme->inputRect(size()));
137 m_letterEdit->setFocus();
138 }
139
game()140 void KLettresView::game()
141 {
142 m_cursorPos = 1;
143 //reset everything so when you change language or levels
144 //it all restarts nicely
145 QObject::disconnect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
146 m_letterEdit->clear();
147 m_letterEdit->setCursorPosition(0);
148 m_letterEdit->setMaxLength( 1 );
149 m_letterEdit->setFocus();
150 m_upperLetter.clear();
151 chooseSound();
152 randomInt++;
153 QObject::connect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
154 }
155
slotProcess(const QString & inputLetter)156 void KLettresView::slotProcess(const QString &inputLetter)
157 {
158 QString lang = Prefs::language();
159 QObject::disconnect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
160
161 //check if backspace
162 if (inputLetter.length() != m_cursorPos) {
163 m_cursorPos--;
164 m_letterEdit->setMaxLength( m_cursorPos );
165 QObject::connect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
166 return;
167 }
168 QChar input_character = inputLetter.at(inputLetter.length()-1);
169 QChar input_character_u;
170 qCDebug(KLETTRES_LOG) << "input_character " << input_character;
171
172 if ((!LangUtils::isIndian(lang) && (input_character.isLetter())) || (LangUtils::isIndian(lang)))
173 {
174 if (input_character.unicode() == 0x00DF) { //everything in upper except the ß
175 input_character_u = input_character.toLower();
176 } else {
177 input_character_u = input_character.toUpper();
178 }
179 m_upperLetter.append(input_character_u);
180 m_letterEdit->selectAll();
181 m_letterEdit->cut();
182 m_letterEdit->setText(m_upperLetter);
183 QTimer::singleShot(m_timer*100, this, &KLettresView::slotTimerDone);
184 } else {
185 qCDebug(KLETTRES_LOG) << "cursor " << m_cursorPos;
186 m_letterEdit->backspace();
187 QObject::connect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
188 }
189
190 }
191
slotTimerDone()192 void KLettresView::slotTimerDone()
193 {
194 QString match = m_currentLetter.left(m_cursorPos );
195 qCDebug(KLETTRES_LOG) << "match " << match.toUpper();
196 qCDebug(KLETTRES_LOG) << "m_upperLetter " << m_upperLetter;
197
198 if (match == m_upperLetter) {
199 if (m_cursorPos!=m_length) {//if text in lineEdit not equal to text on button
200 //i.e if you still have to allow input
201 m_letterEdit->setMaxLength( m_cursorPos +1 );
202 m_letterEdit->setCursorPosition( m_cursorPos );
203 m_letterEdit->setFocus();
204 m_cursorPos ++;
205 QObject::connect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
206 } else {
207 game(); //another syllable
208 }
209 } else { //if not, cut it
210 qCDebug(KLETTRES_LOG) << "wrong letter ";
211 m_letterEdit->backspace(); //delete the char to the left and position curseur accordingly
212 m_upperLetter.remove(m_upperLetter.size()-1, 1);
213 m_letterEdit->setFocus();
214 //play sound again
215 m_klettres->soundFactory->playSound(m_random);
216 QObject::connect(m_letterEdit, &QLineEdit::textChanged, this, &KLettresView::slotProcess);
217 }
218 }
219
slotPlayAgain()220 void KLettresView::slotPlayAgain()
221 {
222 //TODO wait for the previous sound to be payed before playing again as it won't play if the previous one was not finished
223 m_klettres->soundFactory->playSound(m_random);
224 }
225
keyReleaseEvent(QKeyEvent * e)226 void KLettresView::keyReleaseEvent(QKeyEvent * e)
227 {
228 if (e->key() == Qt::Key_Backspace) {
229 m_upperLetter.remove(m_cursorPos-1, 1);
230 }
231 }
232
233
234