1 /***************************************************** vim:set ts=4 sw=4 sts=4:
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>
9 Original author: José Pablo Ezequiel "Pupeno" Fernández
10 Current Maintainer: Gary Cramblitt <garycramblitt@comcast.net>
11 ******************************************************************************/
13 /***************************************************************************
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. *
19 ***************************************************************************/
22 #include <qclipboard.h>
23 #include <qtextstream.h>
24 #include <qtextcodec.h>
29 #include <kapplication.h>
30 #include <kmessagebox.h>
32 #include <kfiledialog.h>
33 #include <dcopclient.h>
34 #include <knotifyclient.h>
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
) :
54 // kdDebug() << "KTTSD::KTTSD Running" << endl;
62 * Create and initialize the SpeechData object.
64 bool KTTSD::initializeSpeechData()
66 // Create speechData object.
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();
90 * Create and initialize the TalkerMgr object.
92 bool KTTSD::initializeTalkerMgr()
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.
104 QString languageCode
= KGlobal::locale()->language();
105 if (m_talkerMgr
->autoconfigureTalker(languageCode
, m_speechData
->config
))
106 load
= m_talkerMgr
->loadPlugIns(m_speechData
->config
);
109 if (m_talkerMgr
->autoconfigureTalker("en", m_speechData
->config
))
110 load
= m_talkerMgr
->loadPlugIns(m_speechData
->config
);
115 // TODO: Would really like to eliminate ALL GUI stuff from kttsd. Find
116 // a better way to do this.
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"),
135 i18n("Do Not Configure"),
136 dontAskConfigureKTTS
) == KMessageBox::Yes
) msgResult
= KMessageBox::Yes
;
138 if (msgResult
== KMessageBox::Yes
) showDialog();
142 m_speechData
->setTalkerMgr(m_talkerMgr
);
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
)));
177 * Terminate speaker thread
180 kdDebug() << "KTTSD::~KTTSD:: Stopping KTTSD service" << endl
;
181 if (m_speaker
) m_speaker
->requestExit();
185 announceEvent("~KTTS", "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
287 ([\\.\\?\\!\\:\\;])\\s
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.
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());
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
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.
360 uint
KTTSD::sayText(const QString
&text
, const QString
&talker
)
362 uint jobNum
= setText(text
, talker
);
363 if (jobNum
) startText(jobNum
);
368 * Adds another part to a text job. Does not start speaking the text.
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.
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.
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
);
419 if ( file
.open(QIODevice::ReadOnly
) )
421 QTextStream
stream(&file
);
422 QString enc
= fixNullString(encoding
);
425 QTextCodec
* codec
= QTextCodec::codecForName(enc
.latin1());
426 if (codec
) stream
.setCodec(codec
);
428 jobNum
= m_speechData
->setText(stream
.read(), fixNullString(talker
), getAppId());
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.
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:
514 QByteArray jobInfo = getTextJobInfo(jobNum);
515 QDataStream stream(jobInfo, QIODevice::ReadOnly);
525 stream >> sentenceCount;
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.
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
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
));
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).
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
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.
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.
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.
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();
797 if ( !text
.isNull() )
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
809 void KTTSD::showDialog()
811 KRun::runCommand("kttsmgr");
817 void KTTSD::kttsdExit()
820 announceEvent("kttsdExit", "kttsdExiting");
831 kdDebug() << "KTTSD::reinit: Running" << endl
;
834 kdDebug() << "KTTSD::reinit: Stopping KTTSD service" << endl
;
835 if (m_speaker
->isSpeakingText()) pauseText();
836 m_speaker
->requestExit();
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.
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");
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() )
885 NotifyOptions notifyOptions
;
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
) )
895 notifyOptions
= notifyEventMap
[ event
];
897 // Check for app-specific default.
898 if ( notifyEventMap
.contains( "default" ) )
901 notifyOptions
= notifyEventMap
[ "default" ];
902 notifyOptions
.eventName
= QString::null
;
906 // If no app-specific action, try default.
909 switch ( m_speechData
->notifyDefaultPresent
)
911 case NotifyPresent::None
:
914 case NotifyPresent::Dialog
:
916 (present
& KNotifyClient::Messagebox
)
918 !(present
& KNotifyClient::PassivePopup
)
921 case NotifyPresent::Passive
:
923 !(present
& KNotifyClient::Messagebox
)
925 (present
& KNotifyClient::PassivePopup
)
928 case NotifyPresent::DialogAndPassive
:
930 (present
& KNotifyClient::Messagebox
)
932 (present
& KNotifyClient::PassivePopup
)
935 case NotifyPresent::All
:
940 notifyOptions
= m_speechData
->notifyDefaultOptions
;
944 int action
= notifyOptions
.action
;
945 talker
= notifyOptions
.talker
;
948 case NotifyAction::DoNotSpeak
:
950 case NotifyAction::SpeakEventName
:
951 if (notifyOptions
.eventName
.isEmpty())
952 msg
= NotifyEvent::getEventName( fromApp
, event
);
954 msg
= notifyOptions
.eventName
;
956 case NotifyAction::SpeakMsg
:
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
) );
968 msg
.replace( "%e", notifyOptions
.eventName
);
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
) {
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
){
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
){
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();
1054 if (client
) appId
= client
->senderId();
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
)
1069 jNum
= m_speechData
->findAJobNumByAppId(getAppId());
1070 if (!jNum
) jNum
= getCurrentTextJob();
1071 if (!jNum
) jNum
= m_speechData
->findAJobNumByAppId(0);
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
;
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
,
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.
1114 kspeech::kspeech(const DCOPCString
& objId
, QObject
*parent
, const char *name
) :
1116 QObject(parent
, name
),
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"