~neon/kmouth/master

1 by Gunnar Schmidt
Imported KMouth into kdeaccessibility
1
/***************************************************************************
486.1.3 by Jeremy Whiting
Updated copyright headers and add missing ones.
2
 *   Copyright (C) 2002 by Gunnar Schmi Dt <kmouth@schmi-dt.de             *
3
 *             (C) 2015 by Jeremy Whiting <jpwhiting@kde.org>              *
1 by Gunnar Schmidt
Imported KMouth into kdeaccessibility
4
 *                                                                         *
5
 *   This program is free software; you can redistribute it and/or modify  *
6
 *   it under the terms of the GNU General Public License as published by  *
7
 *   the Free Software Foundation; either version 2 of the License, or     *
8
 *   (at your option) any later version.                                   *
9
 *                                                                         *
486.1.3 by Jeremy Whiting
Updated copyright headers and add missing ones.
10
 *   This program is distributed in the hope that it will be useful,       *
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13
 *   GNU General Public License for more details.                          *
14
 *                                                                         *
15
 *   You should have received a copy of the GNU General Public License     *
16
 *   along with this program; if not, write to the                         *
17
 *   Free Software Foundation, Inc.,                                       *
18
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
1 by Gunnar Schmidt
Imported KMouth into kdeaccessibility
19
 ***************************************************************************/
20
21
#include "speech.h"
327 by Laurent Montel
Remove not necessary include
22
489 by Jeremy Whiting
Cleanup headers and bump version to 1.2.0
23
#include <QHash>
24
#include <QRegExp>
25
#include <QStack>
26
#include <QTextCodec>
27
#include <QTextStream>
361 by Arto Hytönen
QtModule/QClass fixes
28
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
29
#define macroExpander
30
#include <kmacroexpander.h>
31
32
Speech::Speech()
33
{
34
}
35
36
Speech::~Speech()
37
{
38
}
39
40
QString Speech::prepareCommand(QString command, const QString &text,
41
                               const QString &filename, const QString &language)
42
{
98 by Gunnar Schmidt
Added the possibility to pass the text via a file to the speech synthesizer and modified the code to use the KMacroExpander
43
#ifdef macroExpander
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
44
    QHash<QChar, QString> map;
45
    map[QLatin1Char('t')] = text;
46
    map[QLatin1Char('f')] = filename;
47
    map[QLatin1Char('l')] = language;
48
    return KMacroExpander::expandMacrosShellQuote(command, map);
98 by Gunnar Schmidt
Added the possibility to pass the text via a file to the speech synthesizer and modified the code to use the KMacroExpander
49
#else
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
50
    QStack<bool> stack;  // saved isdoublequote values during parsing of braces
51
    bool issinglequote = false; // inside '...' ?
52
    bool isdoublequote = false; // inside "..." ?
53
    int noreplace = 0; // nested braces when within ${...}
54
    QString escText = K3ShellProcess::quote(text);
55
56
    // character sequences that change the state or need to be otherwise processed
57
    QRegExp re_singlequote("('|%%|%t|%f|%l)");
58
    QRegExp re_doublequote("(\"|\\\\|`|\\$\\(|\\$\\{|%%|%t|%f|%l)");
59
    QRegExp re_noquote("('|\"|\\\\|`|\\$\\(|\\$\\{|\\(|\\{|\\)|\\}|%%|%t|%f|%l)");
60
61
    // parse the command:
62
    for (int i = re_noquote.search(command);
63
         i != -1;
64
         i = (issinglequote ? re_singlequote.search(command, i)
65
              : isdoublequote ? re_doublequote.search(command, i)
66
              : re_noquote.search(command, i))
67
        )
68
        // while there are character sequences that need to be processed
69
    {
70
        if ((command[i] == '(') || (command[i] == '{')) { // (...) or {...}
71
            // assert(isdoublequote == false)
72
            stack.push(isdoublequote);
73
            if (noreplace > 0)
74
                // count nested braces when within ${...}
75
                noreplace++;
76
            i++;
77
        } else if (command[i] == '$') { // $(...) or ${...}
78
            stack.push(isdoublequote);
79
            isdoublequote = false;
80
            if ((noreplace > 0) || (command[i + 1] == '{'))
81
                // count nested braces when within ${...}
82
                noreplace++;
83
            i += 2;
84
        } else if ((command[i] == ')') || (command[i] == '}')) {
85
            // $(...) or (...) or ${...} or {...}
86
            if (!stack.isEmpty())
87
                isdoublequote = stack.pop();
88
            else
89
                qWarning("Parse error.");
90
            if (noreplace > 0)
91
                // count nested braces when within ${...}
92
                noreplace--;
93
            i++;
94
        } else if (command[i] == '\'') {
95
            issinglequote = !issinglequote;
96
            i++;
97
        } else if (command[i] == '"') {
98
            isdoublequote = !isdoublequote;
99
            i++;
100
        } else if (command[i] == '\\')
101
            i += 2;
102
        else if (command[i] == '`') {
103
            // Replace all `...` with safer $(...)
104
            command.replace(i, 1, "$(");
105
            QRegExp re_backticks("(`|\\\\`|\\\\\\\\|\\\\\\$)");
106
            for (int i2 = re_backticks.search(command, i + 2);
107
                 i2 != -1;
108
                 i2 = re_backticks.search(command, i2)
109
                ) {
110
                if (command[i2] == '`') {
111
                    command.replace(i2, 1, ")");
112
                    i2 = command.length(); // leave loop
113
                } else {
114
                    // remove backslash and ignore following character
115
                    command.remove(i2, 1);
116
                    i2++;
117
                }
118
            }
119
            // Leave i unchanged! We need to process "$("
120
        } else if (noreplace > 0) { // do not replace macros within ${...}
121
            if (issinglequote)
122
                i += re_singlequote.matchedLength();
123
            else if (isdoublequote)
124
                i += re_doublequote.matchedLength();
125
            else
126
                i += re_noquote.matchedLength();
127
        } else { // replace macro
128
            QString match, v;
129
130
            // get match
131
            if (issinglequote)
132
                match = re_singlequote.cap();
133
            else if (isdoublequote)
134
                match = re_doublequote.cap();
135
            else
136
                match = re_noquote.cap();
137
138
            // substitute %variables
139
            if (match == "%t")
140
                v = escText;
141
            else if (match == "%f")
142
                v = filename;
143
            else if (match == "%%")
144
                v = "%";
145
            else if (match == "%l")
146
                v = language;
147
148
            // %variable inside of a quote?
149
            if (isdoublequote)
150
                v = '"' + v + '"';
151
            else if (issinglequote)
152
                v = '\'' + v + '\'';
153
154
            command.replace(i, match.length(), v);
155
            i += v.length();
156
        }
157
    }
158
    return command;
98 by Gunnar Schmidt
Added the possibility to pass the text via a file to the speech synthesizer and modified the code to use the KMacroExpander
159
#endif
1 by Gunnar Schmidt
Imported KMouth into kdeaccessibility
160
}
161
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
162
void Speech::speak(QString command, bool stdIn, const QString &text, const QString &language, int encoding, QTextCodec *codec)
163
{
164
    if (text.length() > 0) {
165
        // 1. prepare the text:
166
        // 1.a) encode the text
167
        QTextStream ts(&encText, QIODevice::WriteOnly);
168
        if (encoding == Local)
169
            ts.setCodec(QTextCodec::codecForLocale());
170
        else if (encoding == Latin1)
171
            ts.setCodec("ISO-8859-1");
172
        else if (encoding == Unicode)
173
            ts.setCodec("UTF-16");
174
        else
175
            ts.setCodec(codec);
176
        ts << text;
177
        ts.flush();
178
179
        // 1.b) create a temporary file for the text
180
        tempFile.open();
181
        QTextStream fs(&tempFile);
182
        if (encoding == Local)
183
            fs.setCodec(QTextCodec::codecForLocale());
184
        else if (encoding == Latin1)
185
            fs.setCodec("ISO-8859-1");
186
        else if (encoding == Unicode)
187
            fs.setCodec("UTF-16");
188
        else
189
            fs.setCodec(codec);
190
        fs << text;
191
        fs << endl;
192
        QString filename = tempFile.fileName();
193
        tempFile.flush();
194
195
        // 2. prepare the command:
196
        command = prepareCommand(command, QLatin1String(encText), filename, language);
197
198
        // 3. create a new process
487 by Jeremy Whiting
Initial port to kf5/qt5/QtSpeech.
199
        connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processExited(int, QProcess::ExitStatus)));
200
        //connect(&process, SIGNAL(wroteStdin(K3Process*)), this, SLOT(wroteStdin(K3Process*)));
201
        //connect(&process, SIGNAL(receivedStdout(K3Process*, char*, int)), this, SLOT(receivedStdout(K3Process*, char*, int)));
202
        //connect(&process, SIGNAL(receivedStderr(K3Process*, char*, int)), this, SLOT(receivedStderr(K3Process*, char*, int)));
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
203
204
        // 4. start the process
205
        if (stdIn) {
487 by Jeremy Whiting
Initial port to kf5/qt5/QtSpeech.
206
            m_process.start(command);
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
207
            if (encText.size() > 0)
487 by Jeremy Whiting
Initial port to kf5/qt5/QtSpeech.
208
                m_process.write(encText, encText.size());
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
209
            else
487 by Jeremy Whiting
Initial port to kf5/qt5/QtSpeech.
210
                m_process.close();
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
211
        } else
487 by Jeremy Whiting
Initial port to kf5/qt5/QtSpeech.
212
            m_process.start(command);
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
213
    }
214
}
215
487 by Jeremy Whiting
Initial port to kf5/qt5/QtSpeech.
216
//void Speech::receivedStdout(K3Process *, char *buffer, int buflen)
217
//{
218
//    kDebug() << QString::fromLatin1(buffer, buflen) + QLatin1Char('\n');
219
//}
220
//void Speech::receivedStderr(K3Process *, char *buffer, int buflen)
221
//{
222
//    kDebug() << QString::fromLatin1(buffer, buflen) + QLatin1Char('\n');
223
//}
224
225
//void Speech::wroteStdin(K3Process *)
226
//{
227
//    process.closeStdin();
228
//}
229
230
void Speech::processExited(int exitCode, QProcess::ExitStatus exitStatus)
484.1.19 by Jeremy Whiting
Restyle with astyle as per kdelibs coding style.
231
{
232
    delete this;
1 by Gunnar Schmidt
Imported KMouth into kdeaccessibility
233
}
234
235
#include "speech.moc"