A few new icons...
[kdeaccessibility.git] / kmouth / speech.cpp
blobd2f7db812b237426537bb0f0cedceb3cb070c6c4
1 /***************************************************************************
2 speech.cpp - description
3 -------------------
4 begin : Son Sep 8 2002
5 copyright : (C) 2002 by Gunnar Schmi Dt
6 email : kmouth@schmi-dt.de
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 #include "speech.h"
19 #include <qstring.h>
20 #include <qvaluelist.h>
21 #include <qvaluestack.h>
22 #include <qstringlist.h>
23 #include <qregexp.h>
24 #include <qtextcodec.h>
25 #include <qfile.h>
26 #include <kdebug.h>
28 #include <kdeversion.h>
29 #ifdef KDE_IS_VERSION
30 #if KDE_IS_VERSION(3,2,0)
31 #define macroExpander
32 #include <kmacroexpander.h>
33 #endif
34 #endif
36 Speech::Speech() {
39 Speech::~Speech() {
42 QString Speech::prepareCommand (QString command, const QString &text,
43 const QString &filename, const QString &language) {
44 #ifdef macroExpander
45 QMap<QChar,QString> map;
46 map['t'] = text;
47 map['f'] = filename;
48 map['l'] = language;
49 return KMacroExpander::expandMacrosShellQuote (command, map);
50 #else
51 QValueStack<bool> stack; // saved isdoublequote values during parsing of braces
52 bool issinglequote=false; // inside '...' ?
53 bool isdoublequote=false; // inside "..." ?
54 int noreplace=0; // nested braces when within ${...}
55 QString escText = KShellProcess::quote(text);
57 // character sequences that change the state or need to be otherwise processed
58 QRegExp re_singlequote("('|%%|%t|%f|%l)");
59 QRegExp re_doublequote("(\"|\\\\|`|\\$\\(|\\$\\{|%%|%t|%f|%l)");
60 QRegExp re_noquote ("('|\"|\\\\|`|\\$\\(|\\$\\{|\\(|\\{|\\)|\\}|%%|%t|%f|%l)");
62 // parse the command:
63 for (int i = re_noquote.search(command);
64 i != -1;
65 i = (issinglequote?re_singlequote.search(command,i)
66 :isdoublequote?re_doublequote.search(command,i)
67 :re_noquote.search(command,i))
69 // while there are character sequences that need to be processed
71 if ((command[i]=='(') || (command[i]=='{')) { // (...) or {...}
72 // assert(isdoublequote == false)
73 stack.push(isdoublequote);
74 if (noreplace > 0)
75 // count nested braces when within ${...}
76 noreplace++;
77 i++;
79 else if (command[i]=='$') { // $(...) or ${...}
80 stack.push(isdoublequote);
81 isdoublequote = false;
82 if ((noreplace > 0) || (command[i+1]=='{'))
83 // count nested braces when within ${...}
84 noreplace++;
85 i+=2;
87 else if ((command[i]==')') || (command[i]=='}')) {
88 // $(...) or (...) or ${...} or {...}
89 if (!stack.isEmpty())
90 isdoublequote = stack.pop();
91 else
92 qWarning("Parse error.");
93 if (noreplace > 0)
94 // count nested braces when within ${...}
95 noreplace--;
96 i++;
98 else if (command[i]=='\'') {
99 issinglequote=!issinglequote;
100 i++;
102 else if (command[i]=='"') {
103 isdoublequote=!isdoublequote;
104 i++;
106 else if (command[i]=='\\')
107 i+=2;
108 else if (command[i]=='`') {
109 // Replace all `...` with safer $(...)
110 command.replace (i, 1, "$(");
111 QRegExp re_backticks("(`|\\\\`|\\\\\\\\|\\\\\\$)");
112 for (int i2=re_backticks.search(command,i+2);
113 i2!=-1;
114 i2=re_backticks.search(command,i2)
117 if (command[i2] == '`') {
118 command.replace (i2, 1, ")");
119 i2=command.length(); // leave loop
121 else {
122 // remove backslash and ignore following character
123 command.remove (i2, 1);
124 i2++;
127 // Leave i unchanged! We need to process "$("
129 else if (noreplace > 0) { // do not replace macros within ${...}
130 if (issinglequote)
131 i+=re_singlequote.matchedLength();
132 else if (isdoublequote)
133 i+=re_doublequote.matchedLength();
134 else
135 i+=re_noquote.matchedLength();
137 else { // replace macro
138 QString match, v;
140 // get match
141 if (issinglequote)
142 match=re_singlequote.cap();
143 else if (isdoublequote)
144 match=re_doublequote.cap();
145 else
146 match=re_noquote.cap();
148 // substitute %variables
149 if (match=="%t")
150 v = escText;
151 else if (match=="%f")
152 v = filename;
153 else if (match=="%%")
154 v = "%";
155 else if (match=="%l")
156 v = language;
158 // %variable inside of a quote?
159 if (isdoublequote)
160 v='"'+v+'"';
161 else if (issinglequote)
162 v="'"+v+"'";
164 command.replace (i, match.length(), v);
165 i+=v.length();
168 return command;
169 #endif
172 void Speech::speak(QString command, bool stdIn, const QString &text, const QString &language, int encoding, QTextCodec *codec) {
173 if (text.length () > 0) {
174 // 1. prepare the text:
175 // 1.a) encode the text
176 QTextStream ts (encText, IO_WriteOnly);
177 if (encoding == Local)
178 ts.setEncoding (QTextStream::Locale);
179 else if (encoding == Latin1)
180 ts.setEncoding (QTextStream::Latin1);
181 else if (encoding == Unicode)
182 ts.setEncoding (QTextStream::Unicode);
183 else
184 ts.setCodec (codec);
185 ts << text;
187 // 1.b) create a temporary file for the text
188 tempFile.setAutoDelete(true);
189 QTextStream* fs = tempFile.textStream();
190 if (encoding == Local)
191 fs->setEncoding (QTextStream::Locale);
192 else if (encoding == Latin1)
193 fs->setEncoding (QTextStream::Latin1);
194 else if (encoding == Unicode)
195 fs->setEncoding (QTextStream::Unicode);
196 else
197 fs->setCodec (codec);
198 *fs << text;
199 *fs << endl;
200 QString filename = tempFile.file()->name();
201 tempFile.close();
203 // 2. prepare the command:
204 command = prepareCommand (command, encText, filename, language);
207 // 3. create a new process
208 process << command;
209 connect(&process, SIGNAL(processExited(KProcess *)), this, SLOT(processExited(KProcess *)));
210 connect(&process, SIGNAL(wroteStdin(KProcess *)), this, SLOT(wroteStdin(KProcess *)));
211 connect(&process, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(receivedStdout(KProcess *, char *, int)));
212 connect(&process, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(receivedStderr(KProcess *, char *, int)));
214 // 4. start the process
215 if (stdIn) {
216 process.start(KProcess::NotifyOnExit, KProcess::All);
217 if (encText.size() > 0)
218 process.writeStdin(encText, encText.size());
219 else
220 process.closeStdin();
222 else
223 process.start(KProcess::NotifyOnExit, KProcess::AllOutput);
227 void Speech::receivedStdout (KProcess *, char *buffer, int buflen) {
228 kdDebug() << QString::fromLatin1(buffer, buflen) + "\n";
230 void Speech::receivedStderr (KProcess *, char *buffer, int buflen) {
231 kdDebug() << QString::fromLatin1(buffer, buflen) + "\n";
234 void Speech::wroteStdin(KProcess *) {
235 process.closeStdin();
238 void Speech::processExited(KProcess *) {
239 delete this;
242 #include "speech.moc"