/***************************************************** 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. * * * ***************************************************************************/ // TQt 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 TQCString& objId, TQObject *parent, const char *name) : DCOPObject(objId), TQObject(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, TQT_SIGNAL(textSet(const TQCString&, const uint)), this, TQT_SLOT(slotTextSet(const TQCString&, const uint))); connect (m_speechData, TQT_SIGNAL(textAppended(const TQCString&, const uint, const int)), this, TQT_SLOT(slotTextAppended(const TQCString&, const uint, const int))); connect (m_speechData, TQT_SIGNAL(textRemoved(const TQCString&, const uint)), this, TQT_SLOT(slotTextRemoved(const TQCString&, const uint))); // Hook KNotify signal. if (!connectDCOPSignal(0, 0, "notifySignal(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", "notificationSignal(TQString,TQString,TQString,TQString,TQString,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) { TQString languageCode = TDEGlobal::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. TQString 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, TQT_SIGNAL(textStarted(const TQCString&, const uint)), this, TQT_SLOT(slotTextStarted(const TQCString&, const uint))); connect (m_speaker, TQT_SIGNAL(textFinished(const TQCString&, const uint)), this, TQT_SLOT(slotTextFinished(const TQCString&, const uint))); connect (m_speaker, TQT_SIGNAL(textResumed(const TQCString&, const uint)), this, TQT_SLOT(slotTextResumed(const TQCString&, const uint))); connect (m_speaker, TQT_SIGNAL(sentenceStarted(TQString, TQString, const TQCString&, const uint, const uint)), this, TQT_SLOT(slotSentenceStarted(TQString, TQString, const TQCString&, const uint, const uint))); connect (m_speaker, TQT_SIGNAL(sentenceFinished(const TQCString&, const uint, const uint)), this, TQT_SLOT(slotSentenceFinished(const TQCString&, const uint, const uint))); connect (m_speaker, TQT_SIGNAL(textStopped(const TQCString&, const uint)), this, TQT_SLOT(slotTextStopped(const TQCString&, const uint))); connect (m_speaker, TQT_SIGNAL(textPaused(const TQCString&, const uint)), this, TQT_SLOT(slotTextPaused(const TQCString&, 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 TQString& 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 TQString& /*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 TQString &msg, const TQString &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 TQString &warning, const TQString &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 TQString &message, const TQString &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 TQString &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 TQString &text, const TQString &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 TQString &text, const TQString &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 TQString &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 TQString &filename, const TQString &talker /*=NULL*/, const TQString &encoding /*=NULL*/) { // kdDebug() << "KTTSD::setFile: Running" << endl; if (!m_speaker) return 0; TQFile file(filename); uint jobNum = 0; if ( file.open(IO_ReadOnly) ) { TQTextStream stream(&file); TQString enc = fixNullString(encoding); if (!enc.isEmpty()) { TQTextCodec* codec = TQTextCodec::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. */ TQString KTTSD::getTextJobNumbers() { if (!m_speaker) return TQString(); 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 TQDataStream containing information about the job. * Blank if no such job. * * The stream contains the following elements: * - int state Job state. * - TQCString appId DCOP senderId of the application that requested the speech job. * - TQString 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 TQByteArray jobInfo = getTextJobInfo(jobNum); TQDataStream stream(jobInfo, IO_ReadOnly); int state; TQCString appId; TQString talker; int seq; int sentenceCount; stream >> state; stream >> appId; stream >> talker; stream >> seq; stream >> sentenceCount; @endverbatim */ TQByteArray 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. */ TQString KTTSD::talkerCodeToTalkerId(const TQString& talkerCode) { if (!m_talkerMgr) return TQString(); 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 "". */ TQString 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) { TQString 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 TQStringList of fully-specified talker codes, one * for each talker user has configured. * * @see talkers */ TQStringList KTTSD::getTalkers() { if (!m_talkerMgr) return TQStringList(); 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 TQString &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 */ TQString KTTSD::userDefaultTalker() { if (!m_talkerMgr) return TQString(); 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. TQClipboard *cb = kapp->clipboard(); // Copy text from the clipboard. TQString 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. */ TQString 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 TQString& event, const TQString& fromApp, const TQString &text, const TQString& sound, const TQString& /*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; TQString msg; TQString 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 = TQString(); } } } // 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() ) { TQString fromApps = fromApp + ",knotify"; m_speechData->enqueueMessage( msg, talker, fromApps.utf8() ); m_speaker->doUtterances(); } } } // Slots for the speaker object void KTTSD::slotSentenceStarted(TQString, TQString, const TQCString& 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 TQCString& 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 TQCString& 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 TQCString& 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 TQCString& 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 TQCString& 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 TQCString& 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 TQCString& appId, const uint jobNum){ void KTTSD::slotTextSet(const TQCString& 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 TQCString& 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 TQCString& 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 TQCString KTTSD::getAppId() { DCOPClient* client = callingDcopClient(); TQCString 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 TQString(). */ TQString KTTSD::fixNullString(const TQString &talker) const { if (!talker) return TQString(); if (talker == "0") return TQString(); return talker; } // kspeech is obsolete. Applications should use KSpeech instead. // Constructor. kspeech::kspeech(const TQCString& objId, TQObject *parent, const char *name) : DCOPObject(objId), TQObject(parent, name), m_kttsd("KSpeech") { } // Destructor. kspeech::~kspeech() { } // Delegate all DCOP methods to KTTSD object. /*virtual*/ bool kspeech::supportsMarkup(const TQString &talker, uint markupType) const { return m_kttsd.supportsMarkup(talker, markupType); } /*virtual*/ bool kspeech::supportsMarkers(const TQString &talker) const { return m_kttsd.supportsMarkers(talker); } /*virtual*/ ASYNC kspeech::sayScreenReaderOutput(const TQString &msg, const TQString &talker) { m_kttsd.sayScreenReaderOutput(msg, talker); } /*virtual*/ ASYNC kspeech::sayWarning(const TQString &warning, const TQString &talker) { m_kttsd.sayWarning(warning, talker); } /*virtual*/ ASYNC kspeech::sayMessage(const TQString &message, const TQString &talker) { m_kttsd.sayMessage(message, talker); } /*virtual*/ ASYNC kspeech::setSentenceDelimiter(const TQString &delimiter) { m_kttsd.setSentenceDelimiter(delimiter); } /*virtual*/ uint kspeech::setText(const TQString &text, const TQString &talker) { return m_kttsd.setText(text, talker); } /*virtual*/ uint kspeech::sayText(const TQString &text, const TQString &talker) { return m_kttsd.sayText(text, talker); } /*virtual*/ int kspeech::appendText(const TQString &text, uint jobNum) { return m_kttsd.appendText(text, jobNum); } /*virtual*/ uint kspeech::setFile(const TQString &filename, const TQString &talker, const TQString& 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*/ TQString kspeech::getTextJobNumbers() { return m_kttsd.getTextJobNumbers(); } /*virtual*/ int kspeech::getTextJobState(uint jobNum) { return m_kttsd.getTextJobState(jobNum); } /*virtual*/ TQByteArray kspeech::getTextJobInfo(uint jobNum) { return m_kttsd.getTextJobInfo(jobNum); } /*virtual*/ TQString kspeech::talkerCodeToTalkerId(const TQString& talkerCode) { return m_kttsd.talkerCodeToTalkerId(talkerCode); } /*virtual*/ TQString 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*/ TQStringList kspeech::getTalkers() { return m_kttsd.getTalkers(); } /*virtual*/ ASYNC kspeech::changeTextTalker(const TQString &talker, uint jobNum ) { m_kttsd.changeTextTalker(talker, jobNum); } /*virtual*/ TQString 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*/ TQString kspeech::version() { return m_kttsd.version(); } #include "kttsd.moc"