summaryrefslogtreecommitdiffstats
path: root/kttsd/kttsd/kttsd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kttsd/kttsd/kttsd.cpp')
-rw-r--r--kttsd/kttsd/kttsd.cpp1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/kttsd/kttsd/kttsd.cpp b/kttsd/kttsd/kttsd.cpp
new file mode 100644
index 0000000..9ee841f
--- /dev/null
+++ b/kttsd/kttsd/kttsd.cpp
@@ -0,0 +1,1183 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ KTTSD main class
+ -------------------
+ Copyright:
+ (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
+ (C) 2003-2004 by Olaf Schmidt <ojschmidt@kde.org>
+ (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: José Pablo Ezequiel "Pupeno" Fernández
+ Current Maintainer: Gary Cramblitt <garycramblitt@comcast.net>
+ ******************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+// Qt includes.
+#include <qcstring.h>
+#include <qclipboard.h>
+#include <qtextstream.h>
+#include <qtextcodec.h>
+#include <qfile.h>
+
+// KDE includes.
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <dcopclient.h>
+#include <knotifyclient.h>
+#include <krun.h>
+#include <kaboutdata.h>
+
+// KTTS includes.
+#include "notify.h"
+#include "kttsd.h"
+
+/**
+* This is the "main" module of KTTSD. It performs the following functions:
+* - Creates and destroys SpeechData and Speaker objects.
+* - Receives DCOP calls and dispatches them to SpeechData and Speaker.
+* - Receives signals from SpeechData and Speaker and converts them to DCOP signals.
+*
+* Note that most of the real tts work occurs in Speaker.
+*/
+
+KTTSD::KTTSD(const QCString& objId, QObject *parent, const char *name) :
+ DCOPObject(objId),
+ QObject(parent, name)
+{
+ // kdDebug() << "KTTSD::KTTSD Running" << endl;
+ m_speaker = 0;
+ m_talkerMgr = 0;
+ m_speechData = 0;
+ ready();
+}
+
+/*
+* Create and initialize the SpeechData object.
+*/
+bool KTTSD::initializeSpeechData()
+{
+ // Create speechData object.
+ if (!m_speechData)
+ {
+ m_speechData = new SpeechData();
+ connect (m_speechData, SIGNAL(textSet(const QCString&, const uint)),
+ this, SLOT(slotTextSet(const QCString&, const uint)));
+ connect (m_speechData, SIGNAL(textAppended(const QCString&, const uint, const int)),
+ this, SLOT(slotTextAppended(const QCString&, const uint, const int)));
+ connect (m_speechData, SIGNAL(textRemoved(const QCString&, const uint)),
+ this, SLOT(slotTextRemoved(const QCString&, const uint)));
+
+ // Hook KNotify signal.
+ if (!connectDCOPSignal(0, 0,
+ "notifySignal(QString,QString,QString,QString,QString,int,int,int,int)",
+ "notificationSignal(QString,QString,QString,QString,QString,int,int,int,int)",
+ false)) kdDebug() << "KTTSD:initializeSpeechData: connectDCOPSignal for knotify failed" << endl;
+ }
+ // Load configuration.
+ m_speechData->readConfig();
+
+ return true;
+}
+
+/*
+* Create and initialize the TalkerMgr object.
+*/
+bool KTTSD::initializeTalkerMgr()
+{
+ if (!m_talkerMgr)
+ {
+ if (!m_speechData) initializeSpeechData();
+
+ m_talkerMgr = new TalkerMgr(this, "kttsdtalkermgr");
+ int load = m_talkerMgr->loadPlugIns(m_speechData->config);
+ // If no Talkers configured, try to autoconfigure one, first in the user's
+ // desktop language, but if that fails, fallback to English.
+ if (load < 0)
+ {
+ QString languageCode = KGlobal::locale()->language();
+ if (m_talkerMgr->autoconfigureTalker(languageCode, m_speechData->config))
+ load = m_talkerMgr->loadPlugIns(m_speechData->config);
+ else
+ {
+ if (m_talkerMgr->autoconfigureTalker("en", m_speechData->config))
+ load = m_talkerMgr->loadPlugIns(m_speechData->config);
+ }
+ }
+ if (load < 0)
+ {
+ // TODO: Would really like to eliminate ALL GUI stuff from kttsd. Find
+ // a better way to do this.
+ delete m_speaker;
+ m_speaker = 0;
+ delete m_talkerMgr;
+ m_talkerMgr = 0;
+ delete m_speechData;
+ m_speechData = 0;
+ kdDebug() << "KTTSD::initializeTalkerMgr: no Talkers have been configured." << endl;
+ // Ask if user would like to run configuration dialog, but don't bug user unnecessarily.
+ QString dontAskConfigureKTTS = "DontAskConfigureKTTS";
+ KMessageBox::ButtonCode msgResult;
+ if (KMessageBox::shouldBeShownYesNo(dontAskConfigureKTTS, msgResult))
+ {
+ if (KMessageBox::questionYesNo(
+ 0,
+ i18n("KTTS has not yet been configured. At least one Talker must be configured. "
+ "Would you like to configure it now?"),
+ i18n("KTTS Not Configured"),
+ i18n("Configure"),
+ i18n("Do Not Configure"),
+ dontAskConfigureKTTS) == KMessageBox::Yes) msgResult = KMessageBox::Yes;
+ }
+ if (msgResult == KMessageBox::Yes) showDialog();
+ return false;
+ }
+ }
+ m_speechData->setTalkerMgr(m_talkerMgr);
+ return true;
+}
+
+/*
+* Create and initialize the Speaker object.
+*/
+bool KTTSD::initializeSpeaker()
+{
+ // kdDebug() << "KTTSD::initializeSpeaker: Instantiating Speaker" << endl;
+
+ if (!m_talkerMgr) initializeTalkerMgr();
+
+ // Create speaker object and load plug ins, checking for the return
+ m_speaker = new Speaker(m_speechData, m_talkerMgr);
+ connect (m_speaker, SIGNAL(textStarted(const QCString&, const uint)),
+ this, SLOT(slotTextStarted(const QCString&, const uint)));
+ connect (m_speaker, SIGNAL(textFinished(const QCString&, const uint)),
+ this, SLOT(slotTextFinished(const QCString&, const uint)));
+ connect (m_speaker, SIGNAL(textResumed(const QCString&, const uint)),
+ this, SLOT(slotTextResumed(const QCString&, const uint)));
+ connect (m_speaker, SIGNAL(sentenceStarted(QString, QString, const QCString&, const uint, const uint)),
+ this, SLOT(slotSentenceStarted(QString, QString, const QCString&, const uint, const uint)));
+ connect (m_speaker, SIGNAL(sentenceFinished(const QCString&, const uint, const uint)), this,
+ SLOT(slotSentenceFinished(const QCString&, const uint, const uint)));
+ connect (m_speaker, SIGNAL(textStopped(const QCString&, const uint)),
+ this, SLOT(slotTextStopped(const QCString&, const uint)));
+ connect (m_speaker, SIGNAL(textPaused(const QCString&, const uint)),
+ this, SLOT(slotTextPaused(const QCString&, const uint)));
+
+ return true;
+}
+
+/**
+ * Destructor
+ * Terminate speaker thread
+ */
+KTTSD::~KTTSD(){
+ kdDebug() << "KTTSD::~KTTSD:: Stopping KTTSD service" << endl;
+ if (m_speaker) m_speaker->requestExit();
+ delete m_speaker;
+ delete m_talkerMgr;
+ delete m_speechData;
+ kdDebug() << "KTTSD::~KTTSD: Emitting DCOP signal kttsdExiting()" << endl;
+ kttsdExiting();
+}
+
+/***** DCOP exported functions *****/
+
+/**
+* Determine whether the currently-configured speech plugin supports a speech markup language.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* @param markupType The kttsd code for the desired speech markup language.
+* @return True if the plugin currently configured for the indicated
+* talker supports the indicated speech markup language.
+* @see kttsdMarkupType
+*/
+bool KTTSD::supportsMarkup(const QString& talker /*=NULL*/, const uint markupType /*=0*/) const
+{
+ if (markupType == KSpeech::mtHtml)
+ {
+ if (!m_speechData) return false;
+ return m_speechData->supportsHTML;
+ }
+ if (markupType != KSpeech::mtSsml) return false;
+ if (!m_talkerMgr) return false;
+ return m_talkerMgr->supportsMarkup(fixNullString(talker), markupType);
+}
+
+/**
+* Determine whether the currently-configured speech plugin supports markers in speech markup.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* @return True if the plugin currently configured for the indicated
+* talker supports markers.
+* TODO: Waiting on plugin API.
+*/
+bool KTTSD::supportsMarkers(const QString& /*talker=NULL*/) const { return false; }
+
+/**
+* Say a message as soon as possible, interrupting any other speech in progress.
+* IMPORTANT: This method is reserved for use by Screen Readers and should not be used
+* by any other applications.
+* @param msg The message to be spoken.
+* @param talker Code for the to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* If no plugin has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+*
+* If an existing Screen Reader output is in progress, it is stopped and discarded and
+* replaced with this new message.
+*/
+void KTTSD::sayScreenReaderOutput(const QString &msg, const QString &talker /*=NULL*/)
+{
+ if (!m_speaker) return;
+ m_speechData->setScreenReaderOutput(msg, fixNullString(talker), getAppId());
+ m_speaker->doUtterances();
+}
+
+/**
+* Say a warning. The warning will be spoken when the current sentence
+* stops speaking and takes precedence over Messages and regular text. Warnings should only
+* be used for high-priority messages requiring immediate user attention, such as
+* "WARNING. CPU is overheating."
+* @param warning The warning to be spoken.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* If no plugin has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+*/
+void KTTSD::sayWarning(const QString &warning, const QString &talker /*=NULL*/){
+ // kdDebug() << "KTTSD::sayWarning: Running" << endl;
+ if (!m_speaker) return;
+ kdDebug() << "KTTSD::sayWarning: Adding '" << warning << "' to warning queue." << endl;
+ m_speechData->enqueueWarning(warning, fixNullString(talker), getAppId());
+ m_speaker->doUtterances();
+}
+
+/**
+* Say a message. The message will be spoken when the current sentence stops speaking
+* but after any warnings have been spoken.
+* Messages should be used for one-shot messages that can't wait for
+* normal text messages to stop speaking, such as "You have mail.".
+* @param message The message to be spoken.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* If no talker has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+*/
+void KTTSD::sayMessage(const QString &message, const QString &talker /*=NULL*/)
+{
+ // kdDebug() << "KTTSD::sayMessage: Running" << endl;
+ if (!m_speaker) return;
+ kdDebug() << "KTTSD::sayMessage: Adding '" << message << "' to message queue." << endl;
+ m_speechData->enqueueMessage(message, fixNullString(talker), getAppId());
+ m_speaker->doUtterances();
+}
+
+/**
+* Sets the GREP pattern that will be used as the sentence delimiter.
+* @param delimiter A valid GREP pattern.
+*
+* The default sentence delimiter is
+ @verbatim
+ ([\\.\\?\\!\\:\\;])\\s
+ @endverbatim
+*
+* Note that backward slashes must be escaped.
+*
+* Changing the sentence delimiter does not affect other applications.
+* @see sentenceparsing
+*/
+void KTTSD::setSentenceDelimiter(const QString &delimiter)
+{
+ if (!m_speaker) return;
+ m_speechData->setSentenceDelimiter(fixNullString(delimiter), getAppId());
+}
+
+/**
+* Queue a text job. Does not start speaking the text.
+* @param text The message to be spoken.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default plugin.
+* If no plugin has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+* @return Job number.
+*
+* Plain text is parsed into individual sentences using the current sentence delimiter.
+* Call @ref setSentenceDelimiter to change the sentence delimiter prior to calling setText.
+* Call @ref getTextCount to retrieve the sentence count after calling setText.
+*
+* The text may contain speech mark language, such as Sable, JSML, or SMML,
+* provided that the speech plugin/engine support it. In this case,
+* sentence parsing follows the semantics of the markup language.
+*
+* Call @ref startText to mark the job as speakable and if the
+* job is the first speakable job in the queue, speaking will begin.
+* @see getTextCount
+* @see startText
+*/
+uint KTTSD::setText(const QString &text, const QString &talker /*=NULL*/)
+{
+ // kdDebug() << "KTTSD::setText: Running" << endl;
+ if (!m_speaker) return 0;
+ // kdDebug() << "KTTSD::setText: Setting text: '" << text << "'" << endl;
+ uint jobNum = m_speechData->setText(text, fixNullString(talker), getAppId());
+ return jobNum;
+}
+
+/**
+* Say a plain text job. This is a convenience method that
+* combines @ref setText and @ref startText into a single call.
+* @param text The message to be spoken.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default plugin.
+* If no plugin has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+* @return Job number.
+*
+* Plain text is parsed into individual sentences using the current sentence delimiter.
+* Call @ref setSentenceDelimiter to change the sentence delimiter prior to
+* calling setText.
+* Call @ref getTextCount to retrieve the sentence count after calling setText.
+*
+* The text may contain speech mark language, such as Sable, JSML, or SSML,
+* provided that the speech plugin/engine support it. In this case,
+* sentence parsing follows the semantics of the markup language.
+*
+* The job is marked speakable.
+* If there are other speakable jobs preceeding this one in the queue,
+* those jobs continue speaking and when finished, this job will begin speaking.
+* If there are no other speakable jobs preceeding this one, it begins speaking.
+*
+* @see getTextCount
+*
+* @since KDE 3.5
+*/
+uint KTTSD::sayText(const QString &text, const QString &talker)
+{
+ uint jobNum = setText(text, talker);
+ if (jobNum) startText(jobNum);
+ return jobNum;
+}
+
+/**
+* Adds another part to a text job. Does not start speaking the text.
+* (thread safe)
+* @param text The message to be spoken.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application,
+* but if no such job, applies to the last job queued by any application.
+* @return Part number for the added part. Parts are numbered starting at 1.
+*
+* The text is parsed into individual sentences. Call getTextCount to retrieve
+* the sentence count. Call startText to mark the job as speakable and if the
+* job is the first speakable job in the queue, speaking will begin.
+* @see setText.
+* @see startText.
+*/
+int KTTSD::appendText(const QString &text, const uint jobNum /*=0*/)
+{
+ if (!m_speaker) return 0;
+ return m_speechData->appendText(text, applyDefaultJobNum(jobNum), getAppId());
+}
+
+/**
+* Queue a text job from the contents of a file. Does not start speaking the text.
+* @param filename Full path to the file to be spoken. May be a URL.
+* @param talker Code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* If no plugin has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+* @param encoding Name of the encoding to use when reading the file. If
+* NULL or Empty, uses default stream encoding.
+* @return Job number. 0 if an error occurs.
+*
+* Plain text is parsed into individual sentences using the current sentence delimiter.
+* Call @ref setSentenceDelimiter to change the sentence delimiter prior to calling setText.
+* Call @ref getTextCount to retrieve the sentence count after calling setText.
+*
+* The text may contain speech mark language, such as Sable, JSML, or SMML,
+* provided that the speech plugin/engine support it. In this case,
+* sentence parsing follows the semantics of the markup language.
+*
+* Call @ref startText to mark the job as speakable and if the
+* job is the first speakable job in the queue, speaking will begin.
+* @see getTextCount
+* @see startText
+*/
+uint KTTSD::setFile(const QString &filename, const QString &talker /*=NULL*/,
+ const QString &encoding /*=NULL*/)
+{
+ // kdDebug() << "KTTSD::setFile: Running" << endl;
+ if (!m_speaker) return 0;
+ QFile file(filename);
+ uint jobNum = 0;
+ if ( file.open(IO_ReadOnly) )
+ {
+ QTextStream stream(&file);
+ QString enc = fixNullString(encoding);
+ if (!enc.isEmpty())
+ {
+ QTextCodec* codec = QTextCodec::codecForName(enc.latin1());
+ if (codec) stream.setCodec(codec);
+ }
+ jobNum = m_speechData->setText(stream.read(), fixNullString(talker), getAppId());
+ file.close();
+ }
+ return jobNum;
+}
+
+/**
+* Get the number of sentences in a text job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+* @return The number of sentences in the job. -1 if no such job.
+*
+* The sentences of a job are given sequence numbers from 1 to the number returned by this
+* method. The sequence numbers are emitted in the @ref sentenceStarted and
+* @ref sentenceFinished signals.
+*/
+int KTTSD::getTextCount(const uint jobNum /*=0*/)
+{
+ if (!m_speaker) return -1;
+ return m_speechData->getTextCount(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Get the job number of the current text job.
+* @return Job number of the current text job. 0 if no jobs.
+*
+* Note that the current job may not be speaking. See @ref isSpeakingText.
+* @see getTextJobState.
+* @see isSpeakingText
+*/
+uint KTTSD::getCurrentTextJob()
+{
+ if (!m_speaker) return 0;
+ return m_speaker->getCurrentTextJob();
+}
+
+/**
+* Get the number of jobs in the text job queue.
+* @return Number of text jobs in the queue. 0 if none.
+*/
+uint KTTSD::getTextJobCount()
+{
+ if (!m_speaker) return 0;
+ return m_speechData->getTextJobCount();
+}
+
+/**
+* Get a comma-separated list of text job numbers in the queue.
+* @return Comma-separated list of text job numbers in the queue.
+*/
+QString KTTSD::getTextJobNumbers()
+{
+ if (!m_speaker) return QString::null;
+ return m_speechData->getTextJobNumbers();
+}
+
+/**
+* Get the state of a text job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+* @return State of the job. -1 if invalid job number.
+*
+* @see kttsdJobState
+*/
+int KTTSD::getTextJobState(const uint jobNum /*=0*/)
+{
+ if (!m_speaker) return -1;
+ return m_speechData->getTextJobState(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Get information about a text job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+* @return A QDataStream containing information about the job.
+* Blank if no such job.
+*
+* The stream contains the following elements:
+* - int state Job state.
+* - QCString appId DCOP senderId of the application that requested the speech job.
+* - QString talker Language code in which to speak the text.
+* - int seq Current sentence being spoken. Sentences are numbered starting at 1.
+* - int sentenceCount Total number of sentences in the job.
+*
+* The following sample code will decode the stream:
+ @verbatim
+ QByteArray jobInfo = getTextJobInfo(jobNum);
+ QDataStream stream(jobInfo, IO_ReadOnly);
+ int state;
+ QCString appId;
+ QString talker;
+ int seq;
+ int sentenceCount;
+ stream >> state;
+ stream >> appId;
+ stream >> talker;
+ stream >> seq;
+ stream >> sentenceCount;
+ @endverbatim
+*/
+QByteArray KTTSD::getTextJobInfo(const uint jobNum /*=0*/)
+{
+ return m_speechData->getTextJobInfo(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Given a Talker Code, returns the Talker ID of the talker that would speak
+* a text job with that Talker Code.
+* @param talkerCode Talker Code.
+* @return Talker ID of the talker that would speak the text job.
+*/
+QString KTTSD::talkerCodeToTalkerId(const QString& talkerCode)
+{
+ if (!m_talkerMgr) return QString::null;
+ return m_talkerMgr->talkerCodeToTalkerId(fixNullString(talkerCode));
+}
+
+/**
+* Return a sentence of a job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+* @param seq Sequence number of the sentence.
+* @return The specified sentence in the specified job. If no such
+* job or sentence, returns "".
+*/
+QString KTTSD::getTextJobSentence(const uint jobNum /*=0*/, const uint seq /*=1*/)
+{
+ return m_speechData->getTextJobSentence(applyDefaultJobNum(jobNum), seq);
+}
+
+/**
+* Determine if kttsd is currently speaking any text jobs.
+* @return True if currently speaking any text jobs.
+*/
+bool KTTSD::isSpeakingText() const
+{
+ if (!m_speaker) return false;
+ return m_speaker->isSpeakingText();
+}
+
+/**
+* Remove a text job from the queue.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+*
+* The job is deleted from the queue and the @ref textRemoved signal is emitted.
+*
+* If there is another job in the text queue, and it is marked speakable,
+* that job begins speaking.
+*/
+void KTTSD::removeText(const uint jobNum /*=0*/)
+{
+ kdDebug() << "KTTSD::removeText: Running" << endl;
+ if (!m_speaker) return;
+ m_speaker->removeText(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Start a text job at the beginning.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+*
+* Rewinds the job to the beginning.
+*
+* The job is marked speakable.
+* If there are other speakable jobs preceeding this one in the queue,
+* those jobs continue speaking and when finished, this job will begin speaking.
+* If there are no other speakable jobs preceeding this one, it begins speaking.
+*
+* The @ref textStarted signal is emitted when the text job begins speaking.
+* When all the sentences of the job have been spoken, the job is marked for deletion from
+* the text queue and the @ref textFinished signal is emitted.
+*/
+void KTTSD::startText(const uint jobNum /*=0*/)
+{
+ kdDebug() << "KTTSD::startText: Running" << endl;
+ if (!m_speaker) return;
+ // Determine if we are starting speech.
+ bool startingSpeech = !isSpeakingText();
+ uint jNum = applyDefaultJobNum(jobNum);
+ m_speaker->startText(jNum);
+ // If this has started speech output, determine whether to autostart KTTSMgr.
+ if (startingSpeech)
+ {
+ if (m_speechData->autoStartManager)
+ {
+ // Do not start KTTSMgr unless at least 5 sentences are queued.
+ if (getTextCount(jNum) > 4)
+ {
+ QString cmd = "kttsmgr --systray";
+ if (m_speechData->autoExitManager) cmd.append(" --autoexit");
+ // Notice this fails if KTTSMgr is already running, which is what we want.
+ KRun::runCommand(cmd);
+ }
+ }
+ }
+}
+
+/**
+* Stop a text job and rewind to the beginning.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+*
+* The job is marked not speakable and will not be speakable until @ref startText or @ref resumeText
+* is called.
+*
+* If there are speaking jobs preceeding this one in the queue, they continue speaking.
+* If the job is currently speaking, the @ref textStopped signal is emitted and the job stops speaking.
+* Depending upon the speech engine and plugin used, speeking may not stop immediately
+* (it might finish the current sentence).
+*/
+void KTTSD::stopText(const uint jobNum /*=0*/)
+{
+ kdDebug() << "KTTSD::stopText: Running" << endl;
+ if (!m_speaker) return;
+ m_speaker->stopText(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Pause a text job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+*
+* The job is marked as paused and will not be speakable until @ref resumeText or
+* @ref startText is called.
+*
+* If there are speaking jobs preceeding this one in the queue, they continue speaking.
+* If the job is currently speaking, the @ref textPaused signal is emitted and the job stops speaking.
+* Depending upon the speech engine and plugin used, speeking may not stop immediately
+* (it might finish the current sentence).
+* @see resumeText
+*/
+void KTTSD::pauseText(const uint jobNum /*=0*/)
+{
+ kdDebug() << "KTTSD::pauseText: Running" << endl;
+ if (!m_speaker) return;
+ m_speaker->pauseText(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Start or resume a text job where it was paused.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+*
+* The job is marked speakable.
+*
+* If the job is currently speaking, or is waiting to be spoken (speakable
+* state), the resumeText() call is ignored.
+*
+* If the job is currently queued, or is finished, it is the same as calling
+* @ref startText .
+*
+* If there are speaking jobs preceeding this one in the queue, those jobs continue speaking and,
+* when finished this job will begin speaking where it left off.
+*
+* The @ref textResumed signal is emitted when the job resumes.
+* @see pauseText
+*/
+void KTTSD::resumeText(const uint jobNum /*=0*/)
+{
+ kdDebug() << "KTTSD::resumeText: Running" << endl;
+ if (!m_speaker) return;
+ m_speaker->resumeText(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Get a list of the talkers configured in KTTS.
+* @return A QStringList of fully-specified talker codes, one
+* for each talker user has configured.
+*
+* @see talkers
+*/
+QStringList KTTSD::getTalkers()
+{
+ if (!m_talkerMgr) return QStringList();
+ return m_talkerMgr->getTalkers();
+}
+
+/**
+* Change the talker for a text job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+* @param talker New code for the talker to do the speaking. Example "en".
+* If NULL, defaults to the user's default talker.
+* If no plugin has been configured for the specified Talker code,
+* defaults to the closest matching talker.
+*/
+void KTTSD::changeTextTalker(const QString &talker, uint jobNum)
+{
+ m_speechData->changeTextTalker(fixNullString(talker), applyDefaultJobNum(jobNum));
+}
+
+/**
+* Get the user's default talker.
+* @return A fully-specified talker code.
+*
+* @see talkers
+* @see getTalkers
+*/
+QString KTTSD::userDefaultTalker()
+{
+ if (!m_talkerMgr) return QString::null;
+ return m_talkerMgr->userDefaultTalker();
+}
+
+/**
+* Move a text job down in the queue so that it is spoken later.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application.
+*
+* If the job is currently speaking, it is paused.
+* If the next job in the queue is speakable, it begins speaking.
+*/
+void KTTSD::moveTextLater(const uint jobNum /*=0*/)
+{
+ if (!m_speaker) return;
+ m_speaker->moveTextLater(applyDefaultJobNum(jobNum));
+}
+
+/**
+* Jump to the first sentence of a specified part of a text job.
+* @param partNum Part number of the part to jump to. Parts are numbered starting at 1.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application,
+* but if no such job, applies to the last job queued by any application.
+* @return Part number of the part actually jumped to.
+*
+* If partNum is greater than the number of parts in the job, jumps to last part.
+* If partNum is 0, does nothing and returns the current part number.
+* If no such job, does nothing and returns 0.
+* Does not affect the current speaking/not-speaking state of the job.
+*/
+int KTTSD::jumpToTextPart(const int partNum, const uint jobNum /*=0*/)
+{
+ if (!m_speaker) return 0;
+ return m_speaker->jumpToTextPart(partNum, applyDefaultJobNum(jobNum));
+}
+
+/**
+* Advance or rewind N sentences in a text job.
+* @param n Number of sentences to advance (positive) or rewind (negative) in the job.
+* @param jobNum Job number of the text job.
+* If zero, applies to the last job queued by the application,
+* but if no such job, applies to the last job queued by any application.
+* @return Sequence number of the sentence actually moved to. Sequence numbers
+* are numbered starting at 1.
+*
+* If no such job, does nothing and returns 0.
+* If n is zero, returns the current sequence number of the job.
+* Does not affect the current speaking/not-speaking state of the job.
+*/
+uint KTTSD::moveRelTextSentence(const int n, const uint jobNum /*=0*/)
+{
+ if (!m_speaker) return 0;
+ return m_speaker->moveRelTextSentence(n, applyDefaultJobNum(jobNum));
+}
+
+/**
+* Add the clipboard contents to the text queue and begin speaking it.
+*/
+void KTTSD::speakClipboard()
+{
+ // Get the clipboard object.
+ QClipboard *cb = kapp->clipboard();
+
+ // Copy text from the clipboard.
+ QString text = cb->text();
+
+ // Speak it.
+ if ( !text.isNull() )
+ {
+ setText(text);
+ startText();
+ }
+}
+
+/**
+* Displays the %KTTS Manager dialog. In this dialog, the user may backup or skip forward in
+* any text job by sentence or paragraph, rewind jobs, pause or resume jobs, or
+* delete jobs.
+*/
+void KTTSD::showDialog()
+{
+ KRun::runCommand("kttsmgr");
+}
+
+/**
+* Stop the service.
+*/
+void KTTSD::kttsdExit()
+{
+ stopText();
+ kdDebug() << "KTTSD::kttsdExit: Emitting DCOP signal kttsdExiting()" << endl;
+ kttsdExiting();
+ kapp->quit();
+}
+
+/**
+* Re-start %KTTSD.
+*/
+void KTTSD::reinit()
+{
+ // Restart ourself.
+ kdDebug() << "KTTSD::reinit: Running" << endl;
+ if (m_speaker)
+ {
+ kdDebug() << "KTTSD::reinit: Stopping KTTSD service" << endl;
+ if (m_speaker->isSpeakingText()) pauseText();
+ m_speaker->requestExit();
+ }
+ delete m_speaker;
+ m_speaker = 0;
+ delete m_talkerMgr;
+ m_talkerMgr = 0;
+ ready();
+}
+
+/**
+* Return KTTSD daemon version number.
+*/
+QString KTTSD::version() { return kapp->aboutData()->version(); }
+
+/*
+* Checks if KTTSD is ready to speak and at least one talker is configured.
+* If not, user is prompted to display the configuration dialog.
+*/
+bool KTTSD::ready()
+{
+ if (m_speaker) return true;
+ kdDebug() << "KTTSD::ready: Starting KTTSD service" << endl;
+ if (!initializeSpeechData()) return false;
+ if (!initializeTalkerMgr()) return false;
+ if (!initializeSpeaker()) return false;
+ m_speaker->doUtterances();
+ kdDebug() << "KTTSD::ready: Emitting DCOP signal kttsdStarted()" << endl;
+ kttsdStarted();
+ return true;
+}
+
+void KTTSD::configCommitted() {
+ if (m_speaker) reinit();
+}
+
+/**
+* This signal is emitted by KNotify when a notification event occurs.
+* ds << event << fromApp << text << sound << file << present << level
+* << winId << eventId;
+* default_presentation contains these ORed events: None=0, Sound=1, Messagebox=2, Logfile=4, Stderr=8,
+* PassivePopup=16, Execute=32, Taskbar=64
+*/
+void KTTSD::notificationSignal( const QString& event, const QString& fromApp,
+ const QString &text, const QString& sound, const QString& /*file*/,
+ const int present, const int /*level*/, const int /*windId*/, const int /*eventId*/)
+{
+ if (!m_speaker) return;
+ // kdDebug() << "KTTSD:notificationSignal: event: " << event << " fromApp: " << fromApp <<
+ // " text: " << text << " sound: " << sound << " file: " << file << " present: " << present <<
+ // " level: " << level << " windId: " << windId << " eventId: " << eventId << endl;
+ if ( m_speechData->notify )
+ if ( !m_speechData->notifyExcludeEventsWithSound || sound.isEmpty() )
+ {
+ bool found = false;
+ NotifyOptions notifyOptions;
+ QString msg;
+ QString talker;
+ // Check for app-specific action.
+ if ( m_speechData->notifyAppMap.contains( fromApp ) )
+ {
+ NotifyEventMap notifyEventMap = m_speechData->notifyAppMap[ fromApp ];
+ if ( notifyEventMap.contains( event ) )
+ {
+ found = true;
+ notifyOptions = notifyEventMap[ event ];
+ } else {
+ // Check for app-specific default.
+ if ( notifyEventMap.contains( "default" ) )
+ {
+ found = true;
+ notifyOptions = notifyEventMap[ "default" ];
+ notifyOptions.eventName = QString::null;
+ }
+ }
+ }
+ // If no app-specific action, try default.
+ if ( !found )
+ {
+ switch ( m_speechData->notifyDefaultPresent )
+ {
+ case NotifyPresent::None:
+ found = false;
+ break;
+ case NotifyPresent::Dialog:
+ found = (
+ (present & KNotifyClient::Messagebox)
+ &&
+ !(present & KNotifyClient::PassivePopup)
+ );
+ break;
+ case NotifyPresent::Passive:
+ found = (
+ !(present & KNotifyClient::Messagebox)
+ &&
+ (present & KNotifyClient::PassivePopup)
+ );
+ break;
+ case NotifyPresent::DialogAndPassive:
+ found = (
+ (present & KNotifyClient::Messagebox)
+ &&
+ (present & KNotifyClient::PassivePopup)
+ );
+ break;
+ case NotifyPresent::All:
+ found = true;
+ break;
+ }
+ if ( found )
+ notifyOptions = m_speechData->notifyDefaultOptions;
+ }
+ if ( found )
+ {
+ int action = notifyOptions.action;
+ talker = notifyOptions.talker;
+ switch ( action )
+ {
+ case NotifyAction::DoNotSpeak:
+ break;
+ case NotifyAction::SpeakEventName:
+ if (notifyOptions.eventName.isEmpty())
+ msg = NotifyEvent::getEventName( fromApp, event );
+ else
+ msg = notifyOptions.eventName;
+ break;
+ case NotifyAction::SpeakMsg:
+ msg = text;
+ break;
+ case NotifyAction::SpeakCustom:
+ msg = notifyOptions.customMsg;
+ msg.replace( "%a", fromApp );
+ msg.replace( "%m", text );
+ if ( msg.contains( "%e" ) )
+ {
+ if ( notifyOptions.eventName.isEmpty() )
+ msg.replace( "%e", NotifyEvent::getEventName( fromApp, event ) );
+ else
+ msg.replace( "%e", notifyOptions.eventName );
+ }
+ break;
+ }
+ }
+ // Queue msg if we should speak something.
+ if ( !msg.isEmpty() )
+ {
+ QString fromApps = fromApp + ",knotify";
+ m_speechData->enqueueMessage( msg, talker, fromApps.utf8() );
+ m_speaker->doUtterances();
+ }
+ }
+}
+
+// Slots for the speaker object
+void KTTSD::slotSentenceStarted(QString, QString, const QCString& appId,
+ const uint jobNum, const uint seq) {
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotSentenceStarted: Emitting DCOP signal sentenceStarted with appId " << appId << " job number " << jobNum << " seq number " << seq << endl;
+ sentenceStarted(appId, jobNum, seq);
+}
+
+void KTTSD::slotSentenceFinished(const QCString& appId, const uint jobNum, const uint seq){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotSentenceFinished: Emitting DCOP signal sentenceFinished with appId " << appId << " job number " << jobNum << " seq number " << seq << endl;
+ sentenceFinished(appId, jobNum, seq);
+}
+
+// Slots for the speechData and speaker objects.
+void KTTSD::slotTextStarted(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextStarted: Emitting DCOP signal textStarted with appId " << appId << " job number " << jobNum << endl;
+ textStarted(appId, jobNum);
+}
+
+void KTTSD::slotTextFinished(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextFinished: Emitting DCOP signal textFinished with appId " << appId << " job number " << jobNum << endl;
+ textFinished(appId, jobNum);
+}
+
+void KTTSD::slotTextStopped(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextStopped: Emitting DCOP signal textStopped with appId " << appId << " job number " << jobNum << endl;
+ textStopped(appId, jobNum);
+}
+
+void KTTSD::slotTextPaused(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextPaused: Emitting DCOP signal textPaused with appId " << appId << " job number " << jobNum << endl;
+ textPaused(appId, jobNum);
+}
+
+void KTTSD::slotTextResumed(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextResumed: Emitting DCOP signal textResumed with appId " << appId << " job number " << jobNum << endl;
+ textResumed(appId, jobNum);
+}
+
+//void KTTSD::slotTextSet(const QCString& appId, const uint jobNum){
+void KTTSD::slotTextSet(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextSet: Emitting DCOP signal textSet with appId " << appId << " job number " << jobNum << endl;
+ textSet(appId, jobNum);
+}
+
+void KTTSD::slotTextAppended(const QCString& appId, const uint jobNum, const int partNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextAppended: Emitting DCOP signal textAppended with appId " <<
+ appId << " job number " << jobNum << " part number " << partNum << endl;
+ textAppended(appId, jobNum, partNum);
+}
+
+void KTTSD::slotTextRemoved(const QCString& appId, const uint jobNum){
+ // Emit DCOP signal.
+ kdDebug() << "KTTSD::slotTextRemoved: Emitting DCOP signal textRemoved with appId " << appId << " job number " << jobNum << endl;
+ textRemoved(appId, jobNum);
+}
+
+/**
+ * Returns the senderId (appId) of the DCOP application that called us.
+ * @return The DCOP sendId of calling application.
+ */
+const QCString KTTSD::getAppId()
+{
+ DCOPClient* client = callingDcopClient();
+ QCString appId;
+ if (client) appId = client->senderId();
+ return appId;
+}
+
+/**
+* If a job number is 0, returns the default job number for a command.
+* Returns the job number of the last job queued by the application, or if
+* no such job, the current job number.
+* @return Default job number. 0 if no such job.
+*/
+uint KTTSD::applyDefaultJobNum(const uint jobNum)
+{
+ uint jNum = jobNum;
+ if (!jNum)
+ {
+ jNum = m_speechData->findAJobNumByAppId(getAppId());
+ if (!jNum) jNum = getCurrentTextJob();
+ if (!jNum) jNum = m_speechData->findAJobNumByAppId(0);
+ }
+ return jNum;
+}
+
+/*
+* Fixes a string argument passed in via dcop.
+* If NULL or "0" return QString::null.
+*/
+QString KTTSD::fixNullString(const QString &talker) const
+{
+ if (!talker) return QString::null;
+ if (talker == "0") return QString::null;
+ return talker;
+}
+
+// kspeech is obsolete. Applications should use KSpeech instead.
+
+// Constructor.
+kspeech::kspeech(const QCString& objId, QObject *parent, const char *name) :
+ DCOPObject(objId),
+ QObject(parent, name),
+ m_kttsd("KSpeech")
+{
+}
+
+// Destructor.
+kspeech::~kspeech() { }
+
+// Delegate all DCOP methods to KTTSD object.
+/*virtual*/ bool kspeech::supportsMarkup(const QString &talker, uint markupType) const
+ { return m_kttsd.supportsMarkup(talker, markupType); }
+/*virtual*/ bool kspeech::supportsMarkers(const QString &talker) const
+ { return m_kttsd.supportsMarkers(talker); }
+/*virtual*/ ASYNC kspeech::sayScreenReaderOutput(const QString &msg, const QString &talker)
+ { m_kttsd.sayScreenReaderOutput(msg, talker); }
+/*virtual*/ ASYNC kspeech::sayWarning(const QString &warning, const QString &talker)
+ { m_kttsd.sayWarning(warning, talker); }
+/*virtual*/ ASYNC kspeech::sayMessage(const QString &message, const QString &talker)
+ { m_kttsd.sayMessage(message, talker); }
+/*virtual*/ ASYNC kspeech::setSentenceDelimiter(const QString &delimiter)
+ { m_kttsd.setSentenceDelimiter(delimiter); }
+/*virtual*/ uint kspeech::setText(const QString &text, const QString &talker)
+ { return m_kttsd.setText(text, talker); }
+/*virtual*/ uint kspeech::sayText(const QString &text, const QString &talker)
+ { return m_kttsd.sayText(text, talker); }
+/*virtual*/ int kspeech::appendText(const QString &text, uint jobNum)
+ { return m_kttsd.appendText(text, jobNum); }
+/*virtual*/ uint kspeech::setFile(const QString &filename, const QString &talker,
+ const QString& encoding)
+ { return m_kttsd.setFile(filename, talker, encoding); }
+/*virtual*/ int kspeech::getTextCount(uint jobNum)
+ { return m_kttsd.getTextCount(jobNum); }
+/*virtual*/ uint kspeech::getCurrentTextJob()
+ { return m_kttsd.getCurrentTextJob(); }
+/*virtual*/ uint kspeech::getTextJobCount()
+ { return m_kttsd.getTextJobCount(); }
+/*virtual*/ QString kspeech::getTextJobNumbers()
+ { return m_kttsd.getTextJobNumbers(); }
+/*virtual*/ int kspeech::getTextJobState(uint jobNum)
+ { return m_kttsd.getTextJobState(jobNum); }
+/*virtual*/ QByteArray kspeech::getTextJobInfo(uint jobNum)
+ { return m_kttsd.getTextJobInfo(jobNum); }
+/*virtual*/ QString kspeech::talkerCodeToTalkerId(const QString& talkerCode)
+ { return m_kttsd.talkerCodeToTalkerId(talkerCode); }
+/*virtual*/ QString kspeech::getTextJobSentence(uint jobNum, uint seq)
+ { return m_kttsd.getTextJobSentence(jobNum, seq); }
+/*virtual*/ bool kspeech::isSpeakingText() const
+ { return m_kttsd.isSpeakingText(); }
+/*virtual*/ ASYNC kspeech::removeText(uint jobNum)
+ { m_kttsd.removeText(jobNum); }
+/*virtual*/ ASYNC kspeech::startText(uint jobNum)
+ { m_kttsd.startText(jobNum); }
+/*virtual*/ ASYNC kspeech::stopText(uint jobNum)
+ { m_kttsd.stopText(jobNum); }
+/*virtual*/ ASYNC kspeech::pauseText(uint jobNum)
+ { m_kttsd.pauseText(jobNum); }
+/*virtual*/ ASYNC kspeech::resumeText(uint jobNum)
+ { m_kttsd.resumeText(jobNum); }
+/*virtual*/ QStringList kspeech::getTalkers()
+ { return m_kttsd.getTalkers(); }
+/*virtual*/ ASYNC kspeech::changeTextTalker(const QString &talker, uint jobNum )
+ { m_kttsd.changeTextTalker(talker, jobNum); }
+/*virtual*/ QString kspeech::userDefaultTalker()
+ { return m_kttsd.userDefaultTalker(); }
+/*virtual*/ ASYNC kspeech::moveTextLater(uint jobNum)
+ { m_kttsd.moveTextLater(jobNum); }
+/*virtual*/ int kspeech::jumpToTextPart(int partNum, uint jobNum)
+ { return m_kttsd.jumpToTextPart(partNum, jobNum); }
+/*virtual*/ uint kspeech::moveRelTextSentence(int n, uint jobNum)
+ { return m_kttsd.moveRelTextSentence(n, jobNum); }
+/*virtual*/ ASYNC kspeech::speakClipboard()
+ { m_kttsd.speakClipboard(); }
+/*virtual*/ void kspeech::showDialog()
+ { m_kttsd.showDialog(); }
+/*virtual*/ void kspeech::kttsdExit()
+ { m_kttsd.kttsdExit(); }
+/*virtual*/ void kspeech::reinit()
+ { m_kttsd.reinit(); }
+/*virtual*/ QString kspeech::version()
+ { return m_kttsd.version(); }
+
+#include "kttsd.moc"
+