qt3support--
[kdeaccessibility.git] / kttsd / kttsd / kttsd.cpp
blob9a3f50903c992a624cd0be2662557f922452723f
1 /***************************************************** vim:set ts=4 sw=4 sts=4:
2 KTTSD main class
3 -------------------
4 Copyright:
5 (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
6 (C) 2003-2004 by Olaf Schmidt <ojschmidt@kde.org>
7 (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
8 -------------------
9 Original author: José Pablo Ezequiel "Pupeno" Fernández
10 Current Maintainer: Gary Cramblitt <garycramblitt@comcast.net>
11 ******************************************************************************/
13 /***************************************************************************
14 * *
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; version 2 of the License. *
18 * *
19 ***************************************************************************/
21 // Qt includes.
22 #include <qclipboard.h>
23 #include <qtextstream.h>
24 #include <qtextcodec.h>
25 #include <qfile.h>
27 // KDE includes.
28 #include <kdebug.h>
29 #include <kapplication.h>
30 #include <kmessagebox.h>
31 #include <klocale.h>
32 #include <kfiledialog.h>
33 #include <dcopclient.h>
34 #include <knotifyclient.h>
35 #include <krun.h>
37 // KTTS includes.
38 #include "notify.h"
39 #include "kttsd.h"
41 /**
42 * This is the "main" module of KTTSD. It performs the following functions:
43 * - Creates and destroys SpeechData and Speaker objects.
44 * - Receives DCOP calls and dispatches them to SpeechData and Speaker.
45 * - Receives signals from SpeechData and Speaker and converts them to DCOP signals.
47 * Note that most of the real tts work occurs in Speaker.
50 KTTSD::KTTSD(const DCOPCString& objId, QObject *parent, const char *name) :
51 DCOPObject(objId),
52 QObject(parent, name)
54 // kdDebug() << "KTTSD::KTTSD Running" << endl;
55 m_speaker = 0;
56 m_talkerMgr = 0;
57 m_speechData = 0;
58 ready();
62 * Create and initialize the SpeechData object.
64 bool KTTSD::initializeSpeechData()
66 // Create speechData object.
67 if (!m_speechData)
69 m_speechData = new SpeechData();
70 connect (m_speechData, SIGNAL(textSet(const QByteArray&, const uint)),
71 this, SLOT(slotTextSet(const QByteArray&, const uint)));
72 connect (m_speechData, SIGNAL(textAppended(const QByteArray&, const uint, const int)),
73 this, SLOT(slotTextAppended(const QByteArray&, const uint, const int)));
74 connect (m_speechData, SIGNAL(textRemoved(const QByteArray&, const uint)),
75 this, SLOT(slotTextRemoved(const QByteArray&, const uint)));
77 // Hook KNotify signal.
78 if (!connectDCOPSignal(0, 0,
79 "notifySignal(QString,QString,QString,QString,QString,int,int,int,int)",
80 "notificationSignal(QString,QString,QString,QString,QString,int,int,int,int)",
81 false)) kdDebug() << "KTTSD:initializeSpeechData: connectDCOPSignal for knotify failed" << endl;
83 // Load configuration.
84 m_speechData->readConfig();
86 return true;
90 * Create and initialize the TalkerMgr object.
92 bool KTTSD::initializeTalkerMgr()
94 if (!m_talkerMgr)
96 if (!m_speechData) initializeSpeechData();
98 m_talkerMgr = new TalkerMgr(this, "kttsdtalkermgr");
99 int load = m_talkerMgr->loadPlugIns(m_speechData->config);
100 // If no Talkers configured, try to autoconfigure one, first in the user's
101 // desktop language, but if that fails, fallback to English.
102 if (load < 0)
104 QString languageCode = KGlobal::locale()->language();
105 if (m_talkerMgr->autoconfigureTalker(languageCode, m_speechData->config))
106 load = m_talkerMgr->loadPlugIns(m_speechData->config);
107 else
109 if (m_talkerMgr->autoconfigureTalker("en", m_speechData->config))
110 load = m_talkerMgr->loadPlugIns(m_speechData->config);
113 if (load < 0)
115 // TODO: Would really like to eliminate ALL GUI stuff from kttsd. Find
116 // a better way to do this.
117 delete m_speaker;
118 m_speaker = 0;
119 delete m_talkerMgr;
120 m_talkerMgr = 0;
121 delete m_speechData;
122 m_speechData = 0;
123 kdDebug() << "KTTSD::initializeTalkerMgr: no Talkers have been configured." << endl;
124 // Ask if user would like to run configuration dialog, but don't bug user unnecessarily.
125 QString dontAskConfigureKTTS = "DontAskConfigureKTTS";
126 KMessageBox::ButtonCode msgResult;
127 if (KMessageBox::shouldBeShownYesNo(dontAskConfigureKTTS, msgResult))
129 if (KMessageBox::questionYesNo(
131 i18n("KTTS has not yet been configured. At least one Talker must be configured. "
132 "Would you like to configure it now?"),
133 i18n("KTTS Not Configured"),
134 i18n("Configure"),
135 i18n("Do Not Configure"),
136 dontAskConfigureKTTS) == KMessageBox::Yes) msgResult = KMessageBox::Yes;
138 if (msgResult == KMessageBox::Yes) showDialog();
139 return false;
142 m_speechData->setTalkerMgr(m_talkerMgr);
143 return true;
147 * Create and initialize the Speaker object.
149 bool KTTSD::initializeSpeaker()
151 // kdDebug() << "KTTSD::initializeSpeaker: Instantiating Speaker" << endl;
153 if (!m_talkerMgr) initializeTalkerMgr();
155 // Create speaker object and load plug ins, checking for the return
156 m_speaker = new Speaker(m_speechData, m_talkerMgr);
157 connect (m_speaker, SIGNAL(textStarted(const QByteArray&, const uint)),
158 this, SLOT(slotTextStarted(const QByteArray&, const uint)));
159 connect (m_speaker, SIGNAL(textFinished(const QByteArray&, const uint)),
160 this, SLOT(slotTextFinished(const QByteArray&, const uint)));
161 connect (m_speaker, SIGNAL(textResumed(const QByteArray&, const uint)),
162 this, SLOT(slotTextResumed(const QByteArray&, const uint)));
163 connect (m_speaker, SIGNAL(sentenceStarted(QString, QString, const QByteArray&, const uint, const uint)),
164 this, SLOT(slotSentenceStarted(QString, QString, const QByteArray&, const uint, const uint)));
165 connect (m_speaker, SIGNAL(sentenceFinished(const QByteArray&, const uint, const uint)), this,
166 SLOT(slotSentenceFinished(const QByteArray&, const uint, const uint)));
167 connect (m_speaker, SIGNAL(textStopped(const QByteArray&, const uint)),
168 this, SLOT(slotTextStopped(const QByteArray&, const uint)));
169 connect (m_speaker, SIGNAL(textPaused(const QByteArray&, const uint)),
170 this, SLOT(slotTextPaused(const QByteArray&, const uint)));
172 return true;
176 * Destructor
177 * Terminate speaker thread
179 KTTSD::~KTTSD(){
180 kdDebug() << "KTTSD::~KTTSD:: Stopping KTTSD service" << endl;
181 if (m_speaker) m_speaker->requestExit();
182 delete m_speaker;
183 delete m_talkerMgr;
184 delete m_speechData;
185 announceEvent("~KTTS", "kttsdExiting");
186 kttsdExiting();
189 /***** DCOP exported functions *****/
192 * Determine whether the currently-configured speech plugin supports a speech markup language.
193 * @param talker Code for the talker to do the speaking. Example "en".
194 * If NULL, defaults to the user's default talker.
195 * @param markupType The kttsd code for the desired speech markup language.
196 * @return True if the plugin currently configured for the indicated
197 * talker supports the indicated speech markup language.
198 * @see kttsdMarkupType
200 bool KTTSD::supportsMarkup(const QString& talker /*=NULL*/, const uint markupType /*=0*/) const
202 if (markupType == KSpeech::mtHtml)
204 if (!m_speechData) return false;
205 return m_speechData->supportsHTML;
207 if (markupType != KSpeech::mtSsml) return false;
208 if (!m_talkerMgr) return false;
209 return m_talkerMgr->supportsMarkup(fixNullString(talker), markupType);
213 * Determine whether the currently-configured speech plugin supports markers in speech markup.
214 * @param talker Code for the talker to do the speaking. Example "en".
215 * If NULL, defaults to the user's default talker.
216 * @return True if the plugin currently configured for the indicated
217 * talker supports markers.
218 * TODO: Waiting on plugin API.
220 bool KTTSD::supportsMarkers(const QString& /*talker=NULL*/) const { return false; }
223 * Say a message as soon as possible, interrupting any other speech in progress.
224 * IMPORTANT: This method is reserved for use by Screen Readers and should not be used
225 * by any other applications.
226 * @param msg The message to be spoken.
227 * @param talker Code for the to do the speaking. Example "en".
228 * If NULL, defaults to the user's default talker.
229 * If no plugin has been configured for the specified Talker code,
230 * defaults to the closest matching talker.
232 * If an existing Screen Reader output is in progress, it is stopped and discarded and
233 * replaced with this new message.
235 void KTTSD::sayScreenReaderOutput(const QString &msg, const QString &talker /*=NULL*/)
237 if (!m_speaker) return;
238 m_speechData->setScreenReaderOutput(msg, fixNullString(talker), getAppId());
239 m_speaker->doUtterances();
243 * Say a warning. The warning will be spoken when the current sentence
244 * stops speaking and takes precedence over Messages and regular text. Warnings should only
245 * be used for high-priority messages requiring immediate user attention, such as
246 * "WARNING. CPU is overheating."
247 * @param warning The warning to be spoken.
248 * @param talker Code for the talker to do the speaking. Example "en".
249 * If NULL, defaults to the user's default talker.
250 * If no plugin has been configured for the specified Talker code,
251 * defaults to the closest matching talker.
253 void KTTSD::sayWarning(const QString &warning, const QString &talker /*=NULL*/){
254 // kdDebug() << "KTTSD::sayWarning: Running" << endl;
255 if (!m_speaker) return;
256 kdDebug() << "KTTSD::sayWarning: Adding '" << warning << "' to warning queue." << endl;
257 m_speechData->enqueueWarning(warning, fixNullString(talker), getAppId());
258 m_speaker->doUtterances();
262 * Say a message. The message will be spoken when the current sentence stops speaking
263 * but after any warnings have been spoken.
264 * Messages should be used for one-shot messages that can't wait for
265 * normal text messages to stop speaking, such as "You have mail.".
266 * @param message The message to be spoken.
267 * @param talker Code for the talker to do the speaking. Example "en".
268 * If NULL, defaults to the user's default talker.
269 * If no talker has been configured for the specified Talker code,
270 * defaults to the closest matching talker.
272 void KTTSD::sayMessage(const QString &message, const QString &talker /*=NULL*/)
274 // kdDebug() << "KTTSD::sayMessage: Running" << endl;
275 if (!m_speaker) return;
276 kdDebug() << "KTTSD::sayMessage: Adding '" << message << "' to message queue." << endl;
277 m_speechData->enqueueMessage(message, fixNullString(talker), getAppId());
278 m_speaker->doUtterances();
282 * Sets the GREP pattern that will be used as the sentence delimiter.
283 * @param delimiter A valid GREP pattern.
285 * The default sentence delimiter is
286 @verbatim
287 ([\\.\\?\\!\\:\\;])\\s
288 @endverbatim
290 * Note that backward slashes must be escaped.
292 * Changing the sentence delimiter does not affect other applications.
293 * @see sentenceparsing
295 void KTTSD::setSentenceDelimiter(const QString &delimiter)
297 if (!m_speaker) return;
298 m_speechData->setSentenceDelimiter(fixNullString(delimiter), getAppId());
302 * Queue a text job. Does not start speaking the text.
303 * @param text The message to be spoken.
304 * @param talker Code for the talker to do the speaking. Example "en".
305 * If NULL, defaults to the user's default plugin.
306 * If no plugin has been configured for the specified Talker code,
307 * defaults to the closest matching talker.
308 * @return Job number.
310 * Plain text is parsed into individual sentences using the current sentence delimiter.
311 * Call @ref setSentenceDelimiter to change the sentence delimiter prior to calling setText.
312 * Call @ref getTextCount to retrieve the sentence count after calling setText.
314 * The text may contain speech mark language, such as Sable, JSML, or SMML,
315 * provided that the speech plugin/engine support it. In this case,
316 * sentence parsing follows the semantics of the markup language.
318 * Call @ref startText to mark the job as speakable and if the
319 * job is the first speakable job in the queue, speaking will begin.
320 * @see getTextCount
321 * @see startText
323 uint KTTSD::setText(const QString &text, const QString &talker /*=NULL*/)
325 // kdDebug() << "KTTSD::setText: Running" << endl;
326 if (!m_speaker) return 0;
327 // kdDebug() << "KTTSD::setText: Setting text: '" << text << "'" << endl;
328 uint jobNum = m_speechData->setText(text, fixNullString(talker), getAppId());
329 return jobNum;
333 * Say a plain text job. This is a convenience method that
334 * combines @ref setText and @ref startText into a single call.
335 * @param text The message to be spoken.
336 * @param talker Code for the talker to do the speaking. Example "en".
337 * If NULL, defaults to the user's default plugin.
338 * If no plugin has been configured for the specified Talker code,
339 * defaults to the closest matching talker.
340 * @return Job number.
342 * Plain text is parsed into individual sentences using the current sentence delimiter.
343 * Call @ref setSentenceDelimiter to change the sentence delimiter prior to
344 * calling setText.
345 * Call @ref getTextCount to retrieve the sentence count after calling setText.
347 * The text may contain speech mark language, such as Sable, JSML, or SSML,
348 * provided that the speech plugin/engine support it. In this case,
349 * sentence parsing follows the semantics of the markup language.
351 * The job is marked speakable.
352 * If there are other speakable jobs preceeding this one in the queue,
353 * those jobs continue speaking and when finished, this job will begin speaking.
354 * If there are no other speakable jobs preceeding this one, it begins speaking.
356 * @see getTextCount
358 * @since KDE 3.5
360 uint KTTSD::sayText(const QString &text, const QString &talker)
362 uint jobNum = setText(text, talker);
363 if (jobNum) startText(jobNum);
364 return jobNum;
368 * Adds another part to a text job. Does not start speaking the text.
369 * (thread safe)
370 * @param text The message to be spoken.
371 * @param jobNum Job number of the text job.
372 * If zero, applies to the last job queued by the application,
373 * but if no such job, applies to the last job queued by any application.
374 * @return Part number for the added part. Parts are numbered starting at 1.
376 * The text is parsed into individual sentences. Call getTextCount to retrieve
377 * the sentence count. Call startText to mark the job as speakable and if the
378 * job is the first speakable job in the queue, speaking will begin.
379 * @see setText.
380 * @see startText.
382 int KTTSD::appendText(const QString &text, const uint jobNum /*=0*/)
384 if (!m_speaker) return 0;
385 return m_speechData->appendText(text, applyDefaultJobNum(jobNum), getAppId());
389 * Queue a text job from the contents of a file. Does not start speaking the text.
390 * @param filename Full path to the file to be spoken. May be a URL.
391 * @param talker Code for the talker to do the speaking. Example "en".
392 * If NULL, defaults to the user's default talker.
393 * If no plugin has been configured for the specified Talker code,
394 * defaults to the closest matching talker.
395 * @param encoding Name of the encoding to use when reading the file. If
396 * NULL or Empty, uses default stream encoding.
397 * @return Job number. 0 if an error occurs.
399 * Plain text is parsed into individual sentences using the current sentence delimiter.
400 * Call @ref setSentenceDelimiter to change the sentence delimiter prior to calling setText.
401 * Call @ref getTextCount to retrieve the sentence count after calling setText.
403 * The text may contain speech mark language, such as Sable, JSML, or SMML,
404 * provided that the speech plugin/engine support it. In this case,
405 * sentence parsing follows the semantics of the markup language.
407 * Call @ref startText to mark the job as speakable and if the
408 * job is the first speakable job in the queue, speaking will begin.
409 * @see getTextCount
410 * @see startText
412 uint KTTSD::setFile(const QString &filename, const QString &talker /*=NULL*/,
413 const QString &encoding /*=NULL*/)
415 // kdDebug() << "KTTSD::setFile: Running" << endl;
416 if (!m_speaker) return 0;
417 QFile file(filename);
418 uint jobNum = 0;
419 if ( file.open(QIODevice::ReadOnly) )
421 QTextStream stream(&file);
422 QString enc = fixNullString(encoding);
423 if (!enc.isEmpty())
425 QTextCodec* codec = QTextCodec::codecForName(enc.latin1());
426 if (codec) stream.setCodec(codec);
428 jobNum = m_speechData->setText(stream.read(), fixNullString(talker), getAppId());
429 file.close();
431 return jobNum;
435 * Get the number of sentences in a text job.
436 * @param jobNum Job number of the text job.
437 * If zero, applies to the last job queued by the application.
438 * @return The number of sentences in the job. -1 if no such job.
440 * The sentences of a job are given sequence numbers from 1 to the number returned by this
441 * method. The sequence numbers are emitted in the @ref sentenceStarted and
442 * @ref sentenceFinished signals.
444 int KTTSD::getTextCount(const uint jobNum /*=0*/)
446 if (!m_speaker) return -1;
447 return m_speechData->getTextCount(applyDefaultJobNum(jobNum));
451 * Get the job number of the current text job.
452 * @return Job number of the current text job. 0 if no jobs.
454 * Note that the current job may not be speaking. See @ref isSpeakingText.
455 * @see getTextJobState.
456 * @see isSpeakingText
458 uint KTTSD::getCurrentTextJob()
460 if (!m_speaker) return 0;
461 return m_speaker->getCurrentTextJob();
465 * Get the number of jobs in the text job queue.
466 * @return Number of text jobs in the queue. 0 if none.
468 uint KTTSD::getTextJobCount()
470 if (!m_speaker) return 0;
471 return m_speechData->getTextJobCount();
475 * Get a comma-separated list of text job numbers in the queue.
476 * @return Comma-separated list of text job numbers in the queue.
478 QString KTTSD::getTextJobNumbers()
480 if (!m_speaker) return QString::null;
481 return m_speechData->getTextJobNumbers();
485 * Get the state of a text job.
486 * @param jobNum Job number of the text job.
487 * If zero, applies to the last job queued by the application.
488 * @return State of the job. -1 if invalid job number.
490 * @see kttsdJobState
492 int KTTSD::getTextJobState(const uint jobNum /*=0*/)
494 if (!m_speaker) return -1;
495 return m_speechData->getTextJobState(applyDefaultJobNum(jobNum));
499 * Get information about a text job.
500 * @param jobNum Job number of the text job.
501 * If zero, applies to the last job queued by the application.
502 * @return A QDataStream containing information about the job.
503 * Blank if no such job.
505 * The stream contains the following elements:
506 * - int state Job state.
507 * - DCOPCString appId DCOP senderId of the application that requested the speech job.
508 * - QString talker Language code in which to speak the text.
509 * - int seq Current sentence being spoken. Sentences are numbered starting at 1.
510 * - int sentenceCount Total number of sentences in the job.
512 * The following sample code will decode the stream:
513 @verbatim
514 QByteArray jobInfo = getTextJobInfo(jobNum);
515 QDataStream stream(jobInfo, QIODevice::ReadOnly);
516 int state;
517 DCOPCString appId;
518 QString talker;
519 int seq;
520 int sentenceCount;
521 stream >> state;
522 stream >> appId;
523 stream >> talker;
524 stream >> seq;
525 stream >> sentenceCount;
526 @endverbatim
528 QByteArray KTTSD::getTextJobInfo(const uint jobNum /*=0*/)
530 return m_speechData->getTextJobInfo(applyDefaultJobNum(jobNum));
534 * Given a Talker Code, returns the Talker ID of the talker that would speak
535 * a text job with that Talker Code.
536 * @param talkerCode Talker Code.
537 * @return Talker ID of the talker that would speak the text job.
539 QString KTTSD::talkerCodeToTalkerId(const QString& talkerCode)
541 if (!m_talkerMgr) return QString::null;
542 return m_talkerMgr->talkerCodeToTalkerId(fixNullString(talkerCode));
546 * Return a sentence of a job.
547 * @param jobNum Job number of the text job.
548 * If zero, applies to the last job queued by the application.
549 * @param seq Sequence number of the sentence.
550 * @return The specified sentence in the specified job. If no such
551 * job or sentence, returns "".
553 QString KTTSD::getTextJobSentence(const uint jobNum /*=0*/, const uint seq /*=1*/)
555 return m_speechData->getTextJobSentence(applyDefaultJobNum(jobNum), seq);
559 * Determine if kttsd is currently speaking any text jobs.
560 * @return True if currently speaking any text jobs.
562 bool KTTSD::isSpeakingText() const
564 if (!m_speaker) return false;
565 return m_speaker->isSpeakingText();
569 * Remove a text job from the queue.
570 * @param jobNum Job number of the text job.
571 * If zero, applies to the last job queued by the application.
573 * The job is deleted from the queue and the @ref textRemoved signal is emitted.
575 * If there is another job in the text queue, and it is marked speakable,
576 * that job begins speaking.
578 void KTTSD::removeText(const uint jobNum /*=0*/)
580 kdDebug() << "KTTSD::removeText: Running" << endl;
581 if (!m_speaker) return;
582 m_speaker->removeText(applyDefaultJobNum(jobNum));
586 * Start a text job at the beginning.
587 * @param jobNum Job number of the text job.
588 * If zero, applies to the last job queued by the application.
590 * Rewinds the job to the beginning.
592 * The job is marked speakable.
593 * If there are other speakable jobs preceeding this one in the queue,
594 * those jobs continue speaking and when finished, this job will begin speaking.
595 * If there are no other speakable jobs preceeding this one, it begins speaking.
597 * The @ref textStarted signal is emitted when the text job begins speaking.
598 * When all the sentences of the job have been spoken, the job is marked for deletion from
599 * the text queue and the @ref textFinished signal is emitted.
601 void KTTSD::startText(const uint jobNum /*=0*/)
603 kdDebug() << "KTTSD::startText: Running" << endl;
604 if (!m_speaker) return;
605 // Determine if we are starting speech.
606 bool startingSpeech = !isSpeakingText();
607 uint jNum = applyDefaultJobNum(jobNum);
608 m_speaker->startText(jNum);
609 // If this has started speech output, determine whether to autostart KTTSMgr.
610 if (startingSpeech)
612 if (m_speechData->autoStartManager)
614 // Do not start KTTSMgr unless at least 5 sentences are queued.
615 if (getTextCount(jNum) > 4)
617 QString cmd = "kttsmgr --systray";
618 if (m_speechData->autoExitManager) cmd.append(" --autoexit");
619 // Notice this fails if KTTSMgr is already running, which is what we want.
620 KRun::runCommand(cmd);
627 * Stop a text job and rewind to the beginning.
628 * @param jobNum Job number of the text job.
629 * If zero, applies to the last job queued by the application.
631 * The job is marked not speakable and will not be speakable until @ref startText or @ref resumeText
632 * is called.
634 * If there are speaking jobs preceeding this one in the queue, they continue speaking.
635 * If the job is currently speaking, the @ref textStopped signal is emitted and the job stops speaking.
636 * Depending upon the speech engine and plugin used, speeking may not stop immediately
637 * (it might finish the current sentence).
639 void KTTSD::stopText(const uint jobNum /*=0*/)
641 kdDebug() << "KTTSD::stopText: Running" << endl;
642 if (!m_speaker) return;
643 m_speaker->stopText(applyDefaultJobNum(jobNum));
647 * Pause a text job.
648 * @param jobNum Job number of the text job.
649 * If zero, applies to the last job queued by the application.
651 * The job is marked as paused and will not be speakable until @ref resumeText or
652 * @ref startText is called.
654 * If there are speaking jobs preceeding this one in the queue, they continue speaking.
655 * If the job is currently speaking, the @ref textPaused signal is emitted and the job stops speaking.
656 * Depending upon the speech engine and plugin used, speeking may not stop immediately
657 * (it might finish the current sentence).
658 * @see resumeText
660 void KTTSD::pauseText(const uint jobNum /*=0*/)
662 kdDebug() << "KTTSD::pauseText: Running" << endl;
663 if (!m_speaker) return;
664 m_speaker->pauseText(applyDefaultJobNum(jobNum));
668 * Start or resume a text job where it was paused.
669 * @param jobNum Job number of the text job.
670 * If zero, applies to the last job queued by the application.
672 * The job is marked speakable.
674 * If the job is currently speaking, or is waiting to be spoken (speakable
675 * state), the resumeText() call is ignored.
677 * If the job is currently queued, or is finished, it is the same as calling
678 * @ref startText .
680 * If there are speaking jobs preceeding this one in the queue, those jobs continue speaking and,
681 * when finished this job will begin speaking where it left off.
683 * The @ref textResumed signal is emitted when the job resumes.
684 * @see pauseText
686 void KTTSD::resumeText(const uint jobNum /*=0*/)
688 kdDebug() << "KTTSD::resumeText: Running" << endl;
689 if (!m_speaker) return;
690 m_speaker->resumeText(applyDefaultJobNum(jobNum));
694 * Get a list of the talkers configured in KTTS.
695 * @return A QStringList of fully-specified talker codes, one
696 * for each talker user has configured.
698 * @see talkers
700 QStringList KTTSD::getTalkers()
702 if (!m_talkerMgr) return QStringList();
703 return m_talkerMgr->getTalkers();
707 * Change the talker for a text job.
708 * @param jobNum Job number of the text job.
709 * If zero, applies to the last job queued by the application.
710 * @param talker New code for the talker to do the speaking. Example "en".
711 * If NULL, defaults to the user's default talker.
712 * If no plugin has been configured for the specified Talker code,
713 * defaults to the closest matching talker.
715 void KTTSD::changeTextTalker(const QString &talker, uint jobNum)
717 m_speechData->changeTextTalker(fixNullString(talker), applyDefaultJobNum(jobNum));
721 * Get the user's default talker.
722 * @return A fully-specified talker code.
724 * @see talkers
725 * @see getTalkers
727 QString KTTSD::userDefaultTalker()
729 if (!m_talkerMgr) return QString::null;
730 return m_talkerMgr->userDefaultTalker();
734 * Move a text job down in the queue so that it is spoken later.
735 * @param jobNum Job number of the text job.
736 * If zero, applies to the last job queued by the application.
738 * If the job is currently speaking, it is paused.
739 * If the next job in the queue is speakable, it begins speaking.
741 void KTTSD::moveTextLater(const uint jobNum /*=0*/)
743 if (!m_speaker) return;
744 m_speaker->moveTextLater(applyDefaultJobNum(jobNum));
748 * Jump to the first sentence of a specified part of a text job.
749 * @param partNum Part number of the part to jump to. Parts are numbered starting at 1.
750 * @param jobNum Job number of the text job.
751 * If zero, applies to the last job queued by the application,
752 * but if no such job, applies to the last job queued by any application.
753 * @return Part number of the part actually jumped to.
755 * If partNum is greater than the number of parts in the job, jumps to last part.
756 * If partNum is 0, does nothing and returns the current part number.
757 * If no such job, does nothing and returns 0.
758 * Does not affect the current speaking/not-speaking state of the job.
760 int KTTSD::jumpToTextPart(const int partNum, const uint jobNum /*=0*/)
762 if (!m_speaker) return 0;
763 return m_speaker->jumpToTextPart(partNum, applyDefaultJobNum(jobNum));
767 * Advance or rewind N sentences in a text job.
768 * @param n Number of sentences to advance (positive) or rewind (negative) in the job.
769 * @param jobNum Job number of the text job.
770 * If zero, applies to the last job queued by the application,
771 * but if no such job, applies to the last job queued by any application.
772 * @return Sequence number of the sentence actually moved to. Sequence numbers
773 * are numbered starting at 1.
775 * If no such job, does nothing and returns 0.
776 * If n is zero, returns the current sequence number of the job.
777 * Does not affect the current speaking/not-speaking state of the job.
779 uint KTTSD::moveRelTextSentence(const int n, const uint jobNum /*=0*/)
781 if (!m_speaker) return 0;
782 return m_speaker->moveRelTextSentence(n, applyDefaultJobNum(jobNum));
786 * Add the clipboard contents to the text queue and begin speaking it.
788 void KTTSD::speakClipboard()
790 // Get the clipboard object.
791 QClipboard *cb = kapp->clipboard();
793 // Copy text from the clipboard.
794 QString text = cb->text();
796 // Speak it.
797 if ( !text.isNull() )
799 setText(text);
800 startText();
805 * Displays the %KTTS Manager dialog. In this dialog, the user may backup or skip forward in
806 * any text job by sentence or paragraph, rewind jobs, pause or resume jobs, or
807 * delete jobs.
809 void KTTSD::showDialog()
811 KRun::runCommand("kttsmgr");
815 * Stop the service.
817 void KTTSD::kttsdExit()
819 stopText();
820 announceEvent("kttsdExit", "kttsdExiting");
821 kttsdExiting();
822 kapp->quit();
826 * Re-start %KTTSD.
828 void KTTSD::reinit()
830 // Restart ourself.
831 kdDebug() << "KTTSD::reinit: Running" << endl;
832 if (m_speaker)
834 kdDebug() << "KTTSD::reinit: Stopping KTTSD service" << endl;
835 if (m_speaker->isSpeakingText()) pauseText();
836 m_speaker->requestExit();
838 delete m_speaker;
839 m_speaker = 0;
840 delete m_talkerMgr;
841 m_talkerMgr = 0;
842 ready();
846 * Checks if KTTSD is ready to speak and at least one talker is configured.
847 * If not, user is prompted to display the configuration dialog.
849 bool KTTSD::ready()
851 if (m_speaker) return true;
852 kdDebug() << "KTTSD::ready: Starting KTTSD service" << endl;
853 if (!initializeSpeechData()) return false;
854 if (!initializeTalkerMgr()) return false;
855 if (!initializeSpeaker()) return false;
856 m_speaker->doUtterances();
857 announceEvent("ready", "kttsdStarted");
858 kttsdStarted();
859 return true;
862 void KTTSD::configCommitted() {
863 if (m_speaker) reinit();
867 * This signal is emitted by KNotify when a notification event occurs.
868 * ds << event << fromApp << text << sound << file << present << level
869 * << winId << eventId;
870 * default_presentation contains these ORed events: None=0, Sound=1, Messagebox=2, Logfile=4, Stderr=8,
871 * PassivePopup=16, Execute=32, Taskbar=64
873 void KTTSD::notificationSignal( const QString& event, const QString& fromApp,
874 const QString &text, const QString& sound, const QString& /*file*/,
875 const int present, const int /*level*/, const int /*windId*/, const int /*eventId*/)
877 if (!m_speaker) return;
878 // kdDebug() << "KTTSD:notificationSignal: event: " << event << " fromApp: " << fromApp <<
879 // " text: " << text << " sound: " << sound << " file: " << file << " present: " << present <<
880 // " level: " << level << " windId: " << windId << " eventId: " << eventId << endl;
881 if ( m_speechData->notify )
882 if ( !m_speechData->notifyExcludeEventsWithSound || sound.isEmpty() )
884 bool found = false;
885 NotifyOptions notifyOptions;
886 QString msg;
887 QString talker;
888 // Check for app-specific action.
889 if ( m_speechData->notifyAppMap.contains( fromApp ) )
891 NotifyEventMap notifyEventMap = m_speechData->notifyAppMap[ fromApp ];
892 if ( notifyEventMap.contains( event ) )
894 found = true;
895 notifyOptions = notifyEventMap[ event ];
896 } else {
897 // Check for app-specific default.
898 if ( notifyEventMap.contains( "default" ) )
900 found = true;
901 notifyOptions = notifyEventMap[ "default" ];
902 notifyOptions.eventName = QString::null;
906 // If no app-specific action, try default.
907 if ( !found )
909 switch ( m_speechData->notifyDefaultPresent )
911 case NotifyPresent::None:
912 found = false;
913 break;
914 case NotifyPresent::Dialog:
915 found = (
916 (present & KNotifyClient::Messagebox)
918 !(present & KNotifyClient::PassivePopup)
920 break;
921 case NotifyPresent::Passive:
922 found = (
923 !(present & KNotifyClient::Messagebox)
925 (present & KNotifyClient::PassivePopup)
927 break;
928 case NotifyPresent::DialogAndPassive:
929 found = (
930 (present & KNotifyClient::Messagebox)
932 (present & KNotifyClient::PassivePopup)
934 break;
935 case NotifyPresent::All:
936 found = true;
937 break;
939 if ( found )
940 notifyOptions = m_speechData->notifyDefaultOptions;
942 if ( found )
944 int action = notifyOptions.action;
945 talker = notifyOptions.talker;
946 switch ( action )
948 case NotifyAction::DoNotSpeak:
949 break;
950 case NotifyAction::SpeakEventName:
951 if (notifyOptions.eventName.isEmpty())
952 msg = NotifyEvent::getEventName( fromApp, event );
953 else
954 msg = notifyOptions.eventName;
955 break;
956 case NotifyAction::SpeakMsg:
957 msg = text;
958 break;
959 case NotifyAction::SpeakCustom:
960 msg = notifyOptions.customMsg;
961 msg.replace( "%a", fromApp );
962 msg.replace( "%m", text );
963 if ( msg.contains( "%e" ) )
965 if ( notifyOptions.eventName.isEmpty() )
966 msg.replace( "%e", NotifyEvent::getEventName( fromApp, event ) );
967 else
968 msg.replace( "%e", notifyOptions.eventName );
970 break;
973 // Queue msg if we should speak something.
974 if ( !msg.isEmpty() )
976 QString fromApps = fromApp + ",knotify";
977 m_speechData->enqueueMessage( msg, talker, fromApps.toUtf8() );
978 m_speaker->doUtterances();
983 // Slots for the speaker object
984 void KTTSD::slotSentenceStarted(QString, QString, const QByteArray& appId,
985 const uint jobNum, const uint seq) {
986 // Emit DCOP signal.
987 announceEvent("slotSentenceStarted", "sentenceStarted", appId, jobNum, seq);
988 sentenceStarted(appId, jobNum, seq);
991 void KTTSD::slotSentenceFinished(const QByteArray& appId, const uint jobNum, const uint seq){
992 // Emit DCOP signal.
993 announceEvent("slotSentenceFinished", "sentenceFinished", appId, jobNum, seq);
994 sentenceFinished(appId, jobNum, seq);
997 // Slots for the speechData and speaker objects.
998 void KTTSD::slotTextStarted(const QByteArray& appId, const uint jobNum){
999 // Emit DCOP signal.
1000 announceEvent("slotTextStarted", "textStarted", appId, jobNum);
1001 textStarted(appId, jobNum);
1004 void KTTSD::slotTextFinished(const QByteArray& appId, const uint jobNum){
1005 // Emit DCOP signal.
1006 announceEvent("slotTextFinished", "textFinished", appId, jobNum);
1007 textFinished(appId, jobNum);
1010 void KTTSD::slotTextStopped(const QByteArray& appId, const uint jobNum){
1011 // Emit DCOP signal.
1012 announceEvent("slotTextStopped", "textStopped", appId, jobNum);
1013 textStopped(appId, jobNum);
1016 void KTTSD::slotTextPaused(const QByteArray& appId, const uint jobNum){
1017 // Emit DCOP signal.
1018 announceEvent("slotTextPaused", "textPaused", appId, jobNum);
1019 textPaused(appId, jobNum);
1022 void KTTSD::slotTextResumed(const QByteArray& appId, const uint jobNum){
1023 // Emit DCOP signal.
1024 announceEvent("slotTextResumed", "textResumed", appId, jobNum);
1025 textResumed(appId, jobNum);
1028 void KTTSD::slotTextSet(const QByteArray& appId, const uint jobNum){
1029 // Emit DCOP signal.
1030 announceEvent("slotTextSet", "textSet", appId, jobNum);
1031 textSet(appId, jobNum);
1034 void KTTSD::slotTextAppended(const QByteArray& appId, const uint jobNum, const int partNum){
1035 // Emit DCOP signal.
1036 announceEvent("slotTextAppended", "textAppended", appId, jobNum, partNum);
1037 textAppended(appId, jobNum, partNum);
1040 void KTTSD::slotTextRemoved(const QByteArray& appId, const uint jobNum){
1041 // Emit DCOP signal.
1042 announceEvent("slotTextRemoved", "textRemoved", appId, jobNum);
1043 textRemoved(appId, jobNum);
1047 * Returns the senderId (appId) of the DCOP application that called us.
1048 * @return The DCOP sendId of calling application.
1050 const DCOPCString KTTSD::getAppId()
1052 DCOPClient* client = callingDcopClient();
1053 DCOPCString appId;
1054 if (client) appId = client->senderId();
1055 return appId;
1059 * If a job number is 0, returns the default job number for a command.
1060 * Returns the job number of the last job queued by the application, or if
1061 * no such job, the current job number.
1062 * @return Default job number. 0 if no such job.
1064 uint KTTSD::applyDefaultJobNum(const uint jobNum)
1066 uint jNum = jobNum;
1067 if (!jNum)
1069 jNum = m_speechData->findAJobNumByAppId(getAppId());
1070 if (!jNum) jNum = getCurrentTextJob();
1071 if (!jNum) jNum = m_speechData->findAJobNumByAppId(0);
1073 return jNum;
1077 * Fixes a string argument passed in via dcop.
1078 * If NULL or "0" return QString::null.
1080 QString KTTSD::fixNullString(const QString &talker) const
1082 if (talker == 0) return QString::null;
1083 if (talker == "0") return QString::null;
1084 return talker;
1087 void KTTSD::announceEvent(const QString& slotName, const QString& eventName) {
1088 kdDebug() << "KTTSD::" << slotName << ": emitting DCOP signal " << eventName << endl;
1091 void KTTSD::announceEvent(const QString& slotName, const QString& eventName, const QByteArray appId) {
1092 QString appIdStr(appId);
1093 kdDebug() << "KTTSD::" << slotName << ": emitting DCOP signal " << eventName <<
1094 " with appId " << appIdStr << endl;
1097 void KTTSD::announceEvent(const QString& slotName, const QString& eventName, const QByteArray appId,
1098 uint jobNum) {
1099 QString appIdStr(appId);
1100 kdDebug() << "KTTSD::" << slotName << ": emitting DCOP signal " << eventName <<
1101 " with appId " << appIdStr << " and job number " << jobNum << endl;
1104 void KTTSD::announceEvent(const QString& slotName, const QString& eventName, const QByteArray appId,
1105 uint jobNum, uint seq) {
1106 QString appIdStr(appId);
1107 kdDebug() << "KTTSD::" << slotName << ": emitting DCOP signal " << eventName <<
1108 " with appId " << appIdStr << " job number " << jobNum << " and seq/part number " << seq << endl;
1111 // kspeech is obsolete. Applications should use KSpeech instead.
1113 // Constructor.
1114 kspeech::kspeech(const DCOPCString& objId, QObject *parent, const char *name) :
1115 DCOPObject(objId),
1116 QObject(parent, name),
1117 m_kttsd("KSpeech")
1121 // Destructor.
1122 kspeech::~kspeech() { };
1124 // Delegate all DCOP methods to KTTSD object.
1125 /*virtual*/ bool kspeech::supportsMarkup(const QString &talker, uint markupType) const
1126 { return m_kttsd.supportsMarkup(talker, markupType); }
1127 /*virtual*/ bool kspeech::supportsMarkers(const QString &talker) const
1128 { return m_kttsd.supportsMarkers(talker); }
1129 /*virtual*/ ASYNC kspeech::sayScreenReaderOutput(const QString &msg, const QString &talker)
1130 { m_kttsd.sayScreenReaderOutput(msg, talker); }
1131 /*virtual*/ ASYNC kspeech::sayWarning(const QString &warning, const QString &talker)
1132 { m_kttsd.sayWarning(warning, talker); }
1133 /*virtual*/ ASYNC kspeech::sayMessage(const QString &message, const QString &talker)
1134 { m_kttsd.sayMessage(message, talker); }
1135 /*virtual*/ ASYNC kspeech::setSentenceDelimiter(const QString &delimiter)
1136 { m_kttsd.setSentenceDelimiter(delimiter); }
1137 /*virtual*/ uint kspeech::setText(const QString &text, const QString &talker)
1138 { return m_kttsd.setText(text, talker); }
1139 /*virtual*/ uint kspeech::sayText(const QString &text, const QString &talker)
1140 { return m_kttsd.sayText(text, talker); }
1141 /*virtual*/ int kspeech::appendText(const QString &text, uint jobNum)
1142 { return m_kttsd.appendText(text, jobNum); }
1143 /*virtual*/ uint kspeech::setFile(const QString &filename, const QString &talker,
1144 const QString& encoding)
1145 { return m_kttsd.setFile(filename, talker, encoding); }
1146 /*virtual*/ int kspeech::getTextCount(uint jobNum)
1147 { return m_kttsd.getTextCount(jobNum); }
1148 /*virtual*/ uint kspeech::getCurrentTextJob()
1149 { return m_kttsd.getCurrentTextJob(); }
1150 /*virtual*/ uint kspeech::getTextJobCount()
1151 { return m_kttsd.getTextJobCount(); }
1152 /*virtual*/ QString kspeech::getTextJobNumbers()
1153 { return m_kttsd.getTextJobNumbers(); }
1154 /*virtual*/ int kspeech::getTextJobState(uint jobNum)
1155 { return m_kttsd.getTextJobState(jobNum); }
1156 /*virtual*/ QByteArray kspeech::getTextJobInfo(uint jobNum)
1157 { return m_kttsd.getTextJobInfo(jobNum); }
1158 /*virtual*/ QString kspeech::talkerCodeToTalkerId(const QString& talkerCode)
1159 { return m_kttsd.talkerCodeToTalkerId(talkerCode); }
1160 /*virtual*/ QString kspeech::getTextJobSentence(uint jobNum, uint seq)
1161 { return m_kttsd.getTextJobSentence(jobNum, seq); }
1162 /*virtual*/ bool kspeech::isSpeakingText() const
1163 { return m_kttsd.isSpeakingText(); }
1164 /*virtual*/ ASYNC kspeech::removeText(uint jobNum)
1165 { m_kttsd.removeText(jobNum); }
1166 /*virtual*/ ASYNC kspeech::startText(uint jobNum)
1167 { m_kttsd.startText(jobNum); }
1168 /*virtual*/ ASYNC kspeech::stopText(uint jobNum)
1169 { m_kttsd.stopText(jobNum); }
1170 /*virtual*/ ASYNC kspeech::pauseText(uint jobNum)
1171 { m_kttsd.pauseText(jobNum); }
1172 /*virtual*/ ASYNC kspeech::resumeText(uint jobNum)
1173 { m_kttsd.resumeText(jobNum); }
1174 /*virtual*/ QStringList kspeech::getTalkers()
1175 { return m_kttsd.getTalkers(); }
1176 /*virtual*/ ASYNC kspeech::changeTextTalker(const QString &talker, uint jobNum )
1177 { m_kttsd.changeTextTalker(talker, jobNum); }
1178 /*virtual*/ QString kspeech::userDefaultTalker()
1179 { return m_kttsd.userDefaultTalker(); }
1180 /*virtual*/ ASYNC kspeech::moveTextLater(uint jobNum)
1181 { m_kttsd.moveTextLater(jobNum); }
1182 /*virtual*/ int kspeech::jumpToTextPart(int partNum, uint jobNum)
1183 { return m_kttsd.jumpToTextPart(partNum, jobNum); }
1184 /*virtual*/ uint kspeech::moveRelTextSentence(int n, uint jobNum)
1185 { return m_kttsd.moveRelTextSentence(n, jobNum); }
1186 /*virtual*/ ASYNC kspeech::speakClipboard()
1187 { m_kttsd.speakClipboard(); }
1188 /*virtual*/ void kspeech::showDialog()
1189 { m_kttsd.showDialog(); }
1190 /*virtual*/ void kspeech::kttsdExit()
1191 { m_kttsd.kttsdExit(); }
1192 /*virtual*/ void kspeech::reinit()
1193 { m_kttsd.reinit(); }
1195 #include "kttsd.moc"