From 00bb99ac80741fc50ef8a289719373032f2391eb Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeaccessibility@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kttsd/kttsd/kttsd.cpp | 1183 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1183 insertions(+) create mode 100644 kttsd/kttsd/kttsd.cpp (limited to 'kttsd/kttsd/kttsd.cpp') 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 + (C) 2003-2004 by Olaf Schmidt + (C) 2004 by Gary Cramblitt + ------------------- + Original author: José Pablo Ezequiel "Pupeno" Fernández + Current Maintainer: Gary Cramblitt + ******************************************************************************/ + +/*************************************************************************** + * * + * 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 +#include +#include +#include +#include + +// KDE includes. +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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" + -- cgit v1.2.3