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