SVN_SILENT made messages (.desktop file)
[kdeaccessibility.git] / kttsd / filters / sbd / sbdproc.cpp
blob88824a2415b07ac0ae87f6bba922512e443e623e
1 /***************************************************** vim:set ts=4 sw=4 sts=4:
2 Sentence Boundary Detection Filter class.
3 -------------------
4 Copyright:
5 (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
6 -------------------
7 Original author: Gary Cramblitt <garycramblitt@comcast.net>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 ******************************************************************************/
24 // Qt includes.
25 #include <QtCore/QRegExp>
26 #include <QtXml/qdom.h>
27 #include <QtGui/QApplication>
28 #include <QtCore/QCustomEvent>
30 // KDE includes.
31 #include <kdebug.h>
32 #include <klocale.h>
33 #include <kconfig.h>
35 // KTTS includes.
36 #include "utils.h"
37 #include "talkercode.h"
39 // SdbProc includes.
40 #include "sbdproc.h"
42 /**
43 * Constructor.
45 SbdThread::SbdThread( QObject *parent) :
46 QThread( parent)
50 /**
51 * Destructor.
53 /*virtual*/ SbdThread::~SbdThread()
57 /**
58 * Get/Set text being processed.
60 void SbdThread::setText( const QString& text ) { m_text = text; }
61 QString SbdThread::text() { return m_text; }
63 /**
64 * Set/Get TalkerCode.
66 void SbdThread::setTalkerCode( TalkerCode* talkerCode ) { m_talkerCode = talkerCode; }
67 TalkerCode* SbdThread::talkerCode() { return m_talkerCode; }
69 /**
70 * Set Sentence Boundary Regular Expression.
71 * This method will only be called if the application overrode the default.
73 * @param re The sentence delimiter regular expression.
75 void SbdThread::setSbRegExp( const QString& re ) { m_re = re; }
77 /**
78 * The configured Sentence Boundary Regular Expression.
80 * @param re The sentence delimiter regular expression.
82 void SbdThread::setConfiguredSbRegExp( const QString& re ) { m_configuredRe = re; }
84 /**
85 * The configured Sentence Boundary that replaces SB regular expression.
87 * @param sb The sentence boundary replacement.
90 void SbdThread::setConfiguredSentenceBoundary( const QString& sb ) { m_configuredSentenceBoundary = sb; }
92 /**
93 * Did this filter do anything? If the filter returns the input as output
94 * unmolested, it should return False when this method is called.
96 void SbdThread::setWasModified(bool wasModified) { m_wasModified = wasModified; }
97 bool SbdThread::wasModified() { return m_wasModified; }
99 // Given a tag name, returns SsmlElemType.
100 SbdThread::SsmlElemType SbdThread::tagToSsmlElemType( const QString tagName )
102 if ( tagName == "speak" ) return etSpeak;
103 if ( tagName == "voice" ) return etVoice;
104 if ( tagName == "prosody" ) return etProsody;
105 if ( tagName == "emphasis" ) return etEmphasis;
106 if ( tagName == "break" ) return etBreak;
107 if ( tagName == "s" ) return etPS;
108 if ( tagName == "p" ) return etPS;
109 return etNotSsml;
112 // Parses an SSML element, pushing current settings onto the context stack.
113 void SbdThread::pushSsmlElem( SsmlElemType et, const QDomElement& elem )
115 // TODO: Need to convert relative values into absolute values and also convert
116 // only to values recognized by SSML2SABLE stylesheet. Either that or enhance all
117 // the synth stylesheets.
118 QDomNamedNodeMap attrList = elem.attributes();
119 int attrCount = attrList.count();
120 switch ( et )
122 case etSpeak: {
123 SpeakElem e = m_speakStack.top();
124 for ( int ndx=0; ndx < attrCount; ++ndx )
126 QDomAttr a = attrList.item( ndx ).toAttr();
127 if ( a.name() == "lang" ) e.lang = a.value();
129 m_speakStack.push( e );
130 break; }
131 case etVoice: {
132 VoiceElem e = m_voiceStack.top();
133 // TODO: Since Festival chokes on <voice> tags, don't output them at all.
134 // This means we can't support voice changes, and probably more irritatingly,
135 // gender changes either.
136 m_voiceStack.push( e );
137 break; }
138 case etProsody: {
139 ProsodyElem e = m_prosodyStack.top();
140 for ( int ndx=0; ndx < attrCount; ++ndx )
142 QDomAttr a = attrList.item( ndx ).toAttr();
143 if ( a.name() == "pitch" ) e.pitch = a.value();
144 if ( a.name() == "contour" ) e.contour = a.value();
145 if ( a.name() == "range" ) e.range = a.value();
146 if ( a.name() == "rate" ) e.rate = a.value();
147 if ( a.name() == "duration" ) e.duration = a.value();
148 if ( a.name() == "volume" ) e.volume = a.value();
150 m_prosodyStack.push( e );
151 break; }
152 case etEmphasis: {
153 EmphasisElem e = m_emphasisStack.top();
154 for ( int ndx=0; ndx < attrCount; ++ndx )
156 QDomAttr a = attrList.item( ndx ).toAttr();
157 if ( a.name() == "level" ) e.level = a.value();
159 m_emphasisStack.push( e );
160 break; }
161 case etPS: {
162 PSElem e = m_psStack.top();
163 for ( int ndx=0; ndx < attrCount; ++ndx )
165 QDomAttr a = attrList.item( ndx ).toAttr();
166 if ( a.name() == "lang" ) e.lang = a.value();
168 m_psStack.push( e );
169 break; }
170 default: break;
174 // Given an attribute name and value, constructs an XML representation of the attribute,
175 // i.e., name="value".
176 QString SbdThread::makeAttr( const QString& name, const QString& value )
178 if ( value.isEmpty() ) return QString();
179 return " " + name + "=\"" + value + "\"";
182 // Returns an XML representation of an SSML tag from the top of the context stack.
183 QString SbdThread::makeSsmlElem( SsmlElemType et )
185 QString s;
186 QString a;
187 switch ( et )
189 // Must always output speak tag, otherwise kttsd won't think each sentence is SSML.
190 // For all other tags, only output the tag if it contains at least one attribute.
191 case etSpeak: {
192 SpeakElem e = m_speakStack.top();
193 s = "<speak";
194 if ( !e.lang.isEmpty() ) s += makeAttr( "lang", e.lang );
195 s += '>';
196 break; }
197 case etVoice: {
198 VoiceElem e = m_voiceStack.top();
199 a += makeAttr( "lang", e.lang );
200 a += makeAttr( "gender", e.gender );
201 a += makeAttr( "age", QString::number(e.age) );
202 a += makeAttr( "name", e.name );
203 a += makeAttr( "variant", e.variant );
204 if ( !a.isEmpty() ) s = "<voice" + a + '>';
205 break; }
206 case etProsody: {
207 ProsodyElem e = m_prosodyStack.top();
208 a += makeAttr( "pitch", e.pitch );
209 a += makeAttr( "contour", e.contour );
210 a += makeAttr( "range", e.range );
211 a += makeAttr( "rate", e.rate );
212 a += makeAttr( "duration", e.duration );
213 a += makeAttr( "volume", e.volume );
214 if ( !a.isEmpty() ) s = "<prosody" + a + '>';
215 break; }
216 case etEmphasis: {
217 EmphasisElem e = m_emphasisStack.top();
218 a += makeAttr( "level", e.level );
219 if ( !a.isEmpty() ) s = "<emphasis" + a + '>';
220 break; }
221 case etPS: {
222 break; }
223 default: break;
225 return s;
228 // Pops element from the indicated context stack.
229 void SbdThread::popSsmlElem( SsmlElemType et )
231 switch ( et )
233 case etSpeak: m_speakStack.pop(); break;
234 case etVoice: m_voiceStack.pop(); break;
235 case etProsody: m_prosodyStack.pop(); break;
236 case etEmphasis: m_emphasisStack.pop(); break;
237 case etPS: m_psStack.pop(); break;
238 default: break;
242 // Returns an XML representation of a break element.
243 QString SbdThread::makeBreakElem( const QDomElement& e )
245 QString s = "<break";
246 QDomNamedNodeMap attrList = e.attributes();
247 int attrCount = attrList.count();
248 for ( int ndx=0; ndx < attrCount; ++ndx )
250 QDomAttr a = attrList.item( ndx ).toAttr();
251 s += makeAttr( a.name(), a.value() );
253 s += '>';
254 return s;
257 // Converts a text fragment into a CDATA section.
258 QString SbdThread::makeCDATA( const QString& text )
260 QString s = "<![CDATA[";
261 s += text;
262 s += "]]>";
263 return s;
266 // Returns an XML representation of an utterance node consisting of voice,
267 // prosody, and emphasis elements.
268 QString SbdThread::makeSentence( const QString& text )
270 QString s;
271 QString v = makeSsmlElem( etVoice );
272 QString p = makeSsmlElem( etProsody );
273 QString e = makeSsmlElem( etEmphasis );
274 // TODO: Lang settings from psStack.
275 if ( !v.isEmpty() ) s += v;
276 if ( !p.isEmpty() ) s += p;
277 if ( !e.isEmpty() ) s += e;
278 // Escape ampersands and less thans.
279 QString newText = text;
280 newText.replace(QRegExp("&(?!amp;)"), "&amp;");
281 newText.replace(QRegExp("<(?!lt;)"), "&lt;");
282 s += newText;
283 if ( !e.isEmpty() ) s += "</emphasis>";
284 if ( !p.isEmpty() ) s += "</prosody>";
285 if ( !v.isEmpty() ) s += "</voice>";
286 return s;
289 // Starts a sentence by returning a speak tag.
290 QString SbdThread::startSentence()
292 if ( m_sentenceStarted ) return QString();
293 QString s;
294 s += makeSsmlElem( etSpeak );
295 m_sentenceStarted = true;
296 return s;
299 // Ends a sentence and appends a Tab.
300 QString SbdThread::endSentence()
302 if ( !m_sentenceStarted ) return QString();
303 QString s = "</speak>";
304 s += '\t';
305 m_sentenceStarted = false;
306 return s;
309 // Parses a node of the SSML tree and recursively parses its children.
310 // Returns the filtered text with each sentence a complete ssml tree.
311 QString SbdThread::parseSsmlNode( QDomNode& n, const QString& re )
313 QString result;
314 switch ( n.nodeType() )
316 case QDomNode::ElementNode: { // = 1
317 QDomElement e = n.toElement();
318 QString tagName = e.tagName();
319 SsmlElemType et = tagToSsmlElemType( tagName );
320 switch ( et )
322 case etSpeak:
323 case etVoice:
324 case etProsody:
325 case etEmphasis:
326 case etPS:
328 pushSsmlElem( et, e );
329 QDomNode t = n.firstChild();
330 while ( !t.isNull() )
332 result += parseSsmlNode( t, re );
333 t = t.nextSibling();
335 popSsmlElem( et );
336 if ( et == etPS )
337 result += endSentence();
338 break;
340 case etBreak:
342 // Break elements are empty.
343 result += makeBreakElem( e );
345 // Ignore any elements we don't recognize.
346 default: break;
348 break; }
349 case QDomNode::AttributeNode: { // = 2
350 break; }
351 case QDomNode::TextNode: { // = 3
352 QString s = parsePlainText( n.toText().data(), re );
353 // QString d = s;
354 // d.replace("\t", "\\t");
355 // kDebug() << "SbdThread::parseSsmlNode: parsedPlainText = [" << d << "]";
356 QStringList sentenceList = s.split( '\t', QString::SkipEmptyParts);
357 int lastNdx = sentenceList.count() - 1;
358 for ( int ndx=0; ndx < lastNdx; ++ndx )
360 result += startSentence();
361 result += makeSentence( sentenceList[ndx] );
362 result += endSentence();
364 // Only output sentence boundary if last text fragment ended a sentence.
365 if ( lastNdx >= 0 )
367 result += startSentence();
368 result += makeSentence( sentenceList[lastNdx] );
369 if ( s.endsWith( '\t' ) ) result += endSentence();
371 break; }
372 case QDomNode::CDATASectionNode: { // = 4
373 QString s = parsePlainText( n.toCDATASection().data(), re );
374 QStringList sentenceList = s.split( '\t', QString::SkipEmptyParts);
375 int lastNdx = sentenceList.count() - 1;
376 for ( int ndx=0; ndx < lastNdx; ++ndx )
378 result += startSentence();
379 result += makeSentence( makeCDATA( sentenceList[ndx] ) );
380 result += endSentence();
382 // Only output sentence boundary if last text fragment ended a sentence.
383 if ( lastNdx >= 0 )
385 result += startSentence();
386 result += makeSentence( makeCDATA( sentenceList[lastNdx] ) );
387 if ( s.endsWith( '\t' ) ) result += endSentence();
389 break; }
390 case QDomNode::EntityReferenceNode: { // = 5
391 break; }
392 case QDomNode::EntityNode: { // = 6
393 break; }
394 case QDomNode::ProcessingInstructionNode: { // = 7
395 break; }
396 case QDomNode::CommentNode: { // = 8
397 break; }
398 case QDomNode::DocumentNode: { // = 9
399 break; }
400 case QDomNode::DocumentTypeNode: { // = 10
401 break; }
402 case QDomNode::DocumentFragmentNode: { // = 11
403 break; }
404 case QDomNode::NotationNode: { // = 12
405 break; }
406 case QDomNode::BaseNode: { // = 21
407 break; }
408 case QDomNode::CharacterDataNode: { // = 22
409 break; }
411 return result;
414 // Parses Ssml.
415 QString SbdThread::parseSsml( const QString& inputText, const QString& re )
417 QRegExp sentenceDelimiter = QRegExp( re );
419 // Read the text into xml dom tree.
420 QDomDocument doc( "" );
421 // If an error occurs parsing the SSML, return "invalid S S M L".
422 if ( !doc.setContent( inputText ) ) return i18n("Invalid S S M L.");
424 // Set up context stacks and set defaults for all element attributes.
425 m_speakStack.clear();
426 m_voiceStack.clear();
427 m_prosodyStack.clear();
428 m_emphasisStack.clear();
429 m_psStack.clear();
430 SpeakElem se = { "" };
431 m_speakStack.push ( se );
432 VoiceElem ve = {"", "neutral", 40, "", ""};
433 m_voiceStack.push( ve );
434 ProsodyElem pe = { "medium", "", "medium", "medium", "", "medium" };
435 m_prosodyStack.push( pe );
436 EmphasisElem em = { "" };
437 m_emphasisStack.push( em );
438 PSElem pse = { "" };
439 m_psStack.push ( pse );
441 // This flag is used to close out a previous sentence.
442 m_sentenceStarted = false;
444 // Get the root element (speak) and recursively process its children.
445 QDomElement docElem = doc.documentElement();
446 QDomNode n = docElem.firstChild();
447 QString ssml = parseSsmlNode( docElem, re );
449 // Close out last sentence.
450 if ( m_sentenceStarted ) ssml += "</speak>";
452 return ssml;
455 // Parses code. Each newline is converted into a tab character (\t).
456 QString SbdThread::parseCode( const QString& inputText )
458 QString temp = inputText;
459 // Replace newlines with tabs.
460 temp.replace("\n","\t");
461 // Remove leading spaces.
462 temp.replace(QRegExp("\\t +"), "\t");
463 // Remove trailing spaces.
464 temp.replace(QRegExp(" +\\t"), "\t");
465 // Remove blank lines.
466 temp.replace(QRegExp("\t\t+"),"\t");
467 return temp;
470 // Parses plain text.
471 QString SbdThread::parsePlainText( const QString& inputText, const QString& re )
473 // kDebug() << "SbdThread::parsePlainText: parsing " << inputText << " with re " << re;
474 QRegExp sentenceDelimiter = QRegExp( re );
475 QString temp = inputText;
476 // Replace sentence delimiters with tab.
477 temp.replace(sentenceDelimiter, m_configuredSentenceBoundary);
478 // Replace remaining newlines with spaces.
479 temp.replace("\n"," ");
480 temp.replace("\r"," ");
481 // Remove leading spaces.
482 temp.replace(QRegExp("\\t +"), "\t");
483 // Remove trailing spaces.
484 temp.replace(QRegExp(" +\\t"), "\t");
485 // Remove blank lines.
486 temp.replace(QRegExp("\t\t+"),"\t");
487 // kDebug() << "SbdThread::parsePlainText: returning " << temp;
488 return temp;
491 // This is where the real work takes place.
492 /*virtual*/ void SbdThread::run()
494 // kDebug() << "SbdThread::run: processing text = " << m_text;
496 // TODO: Determine if we should do anything or not.
497 m_wasModified = true;
499 // Determine what kind of input text we are dealing with.
500 int textType;
501 if ( KttsUtils::hasRootElement( m_text, "speak" ) )
502 textType = ttSsml;
503 else
505 // Examine just the first 500 chars to see if it is code.
506 QString p = m_text.left( 500 );
507 if ( p.contains( QRegExp( "(/\\*)|(if\\b\\()|(^#include\\b)" ) ) )
508 textType = ttCode;
509 else
510 textType = ttPlain;
513 // If application specified a sentence delimiter regular expression, use that,
514 // otherwise use configured default.
515 QString re = m_re;
516 if ( re.isEmpty() ) re = m_configuredRe;
518 // Replace spaces, tabs, and formfeeds with a single space.
519 m_text.replace(QRegExp("[ \\t\\f]+"), " ");
521 // kDebug() << "SbdThread::run: textType = " << textType;
523 // Perform the filtering based on type of text.
524 switch ( textType )
526 case ttSsml:
527 m_text = parseSsml( m_text, re );
528 break;
530 case ttCode:
531 m_text = parseCode( m_text );
532 break;
534 case ttPlain:
535 m_text = parsePlainText( m_text, re);
536 break;
539 // Clear app-specified sentence delimiter. App must call setSbRegExp for each conversion.
540 m_re.clear();
542 // kDebug() << "SbdThread::run: filtered text = " << m_text;
544 // Result is in m_text;
546 // Post an event. We need to emit filterFinished signal, but not from the
547 // separate thread.
548 QEvent* ev = new QEvent(QEvent::Type(QEvent::User + 301));
549 QApplication::postEvent(this, ev);
552 bool SbdThread::event ( QEvent * e )
554 if ( e->type() == (QEvent::User + 301) )
556 // kDebug() << "SbdThread::event: emitting filteringFinished signal.";
557 emit filteringFinished();
558 return true;
560 else return false;
563 // ----------------------------------------------------------------------------
566 * Constructor.
568 SbdProc::SbdProc( QObject *parent, const QStringList& /*args*/) :
569 KttsFilterProc(parent)
571 // kDebug() << "SbdProc::SbdProc: Running";
572 m_sbdThread = new SbdThread(this);
573 connect( m_sbdThread, SIGNAL(filteringFinished()), this, SLOT(slotSbdThreadFilteringFinished()) );
577 * Destructor.
579 SbdProc::~SbdProc()
581 // kDebug() << "SbdProc::~SbdProc: Running";
582 if ( m_sbdThread )
584 if ( m_sbdThread->isRunning() )
586 m_sbdThread->terminate();
587 m_sbdThread->wait();
589 delete m_sbdThread;
594 * Initialize the filter.
595 * @param config Settings object.
596 * @param configGroup Settings Group.
597 * @return False if filter is not ready to filter.
599 * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
600 * separate configuration files of their own.
602 bool SbdProc::init(KConfig* c, const QString& configGroup){
603 // kDebug() << "SbdProc::init: Running";
604 KConfigGroup config(c, configGroup );
605 // m_configuredRe = config.readEntry( "SentenceDelimiterRegExp", "([\\.\\?\\!\\:\\;])\\s|(\\n *\\n)" );
606 m_configuredRe = config.readEntry( "SentenceDelimiterRegExp", "([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))" );
607 m_sbdThread->setConfiguredSbRegExp( m_configuredRe );
608 QString sb = config.readEntry( "SentenceBoundary", "\\1\t" );
609 sb.replace( "\\t", "\t" );
610 m_sbdThread->setConfiguredSentenceBoundary( sb );
611 m_appIdList = config.readEntry( "AppID", QStringList(), ',' );
612 m_languageCodeList = config.readEntry( "LanguageCodes", QStringList(), ',' );
613 return true;
617 * Returns True if this filter is a Sentence Boundary Detector.
618 * If so, the filter should implement @ref setSbRegExp() .
619 * @return True if this filter is a SBD.
621 /*virtual*/ bool SbdProc::isSBD() { return true; }
624 * Returns True if the plugin supports asynchronous processing,
625 * i.e., supports asyncConvert method.
626 * @return True if this plugin supports asynchronous processing.
628 * If the plugin returns True, it must also implement @ref getState .
629 * It must also emit @ref filteringFinished when filtering is completed.
630 * If the plugin returns True, it must also implement @ref stopFiltering .
631 * It must also emit @ref filteringStopped when filtering has been stopped.
633 /*virtual*/ bool SbdProc::supportsAsync() { return true; }
636 * Convert input, returning output. Runs synchronously.
637 * @param inputText Input text.
638 * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
639 * use for synthing the text. Useful for extracting hints about
640 * how to filter the text. For example, languageCode.
641 * @param appId The DCOP appId of the application that queued the text.
642 * Also useful for hints about how to do the filtering.
644 /*virtual*/ QString SbdProc::convert(const QString& inputText, TalkerCode* talkerCode,
645 const QString& appId)
647 if ( asyncConvert( inputText, talkerCode, appId) )
649 waitForFinished();
650 // kDebug() << "SbdProc::convert: returning " << getOutput();
651 return getOutput();
652 } else return inputText;
656 * Convert input. Runs asynchronously.
657 * @param inputText Input text.
658 * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
659 * use for synthing the text. Useful for extracting hints about
660 * how to filter the text. For example, languageCode.
661 * @param appId The DCOP appId of the application that queued the text.
662 * Also useful for hints about how to do the filtering.
663 * @return False if the filter cannot perform the conversion.
665 * When conversion is completed, emits signal @ref filteringFinished. Calling
666 * program may then call @ref getOutput to retrieve converted text. Calling
667 * program must call @ref ackFinished to acknowledge the conversion.
669 /*virtual*/ bool SbdProc::asyncConvert(const QString& inputText, TalkerCode* talkerCode,
670 const QString& appId)
672 // kDebug() << "SbdProc::asyncConvert: running";
673 m_sbdThread->setWasModified( false );
674 // If language doesn't match, return input unmolested.
675 if ( !m_languageCodeList.isEmpty() )
677 QString languageCode = talkerCode->languageCode();
678 // kDebug() << "StringReplacerProc::convert: converting " << inputText <<
679 // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
680 if ( !m_languageCodeList.contains( languageCode ) )
682 if ( !talkerCode->countryCode().isEmpty() )
684 languageCode += '_' + talkerCode->countryCode();
685 // kDebug() << "StringReplacerProc::convert: converting " << inputText <<
686 // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
687 if ( !m_languageCodeList.contains( languageCode ) ) return false;
688 } else return false;
691 // If appId doesn't match, return input unmolested.
692 if ( !m_appIdList.isEmpty() )
694 // kDebug() << "SbdProc::convert: converting " << inputText << " if appId "
695 // << appId << " matches " << m_appIdList << endl;
696 bool found = false;
697 QString appIdStr = appId;
698 for ( int ndx=0; ndx < m_appIdList.count(); ++ndx )
700 if ( appIdStr.contains(m_appIdList[ndx]) )
702 found = true;
703 break;
706 if ( !found ) return false;
708 m_sbdThread->setText( inputText );
709 m_sbdThread->setTalkerCode( talkerCode );
710 m_state = fsFiltering;
711 m_sbdThread->start();
712 return true;
716 * Waits for a previous call to asyncConvert to finish.
718 /*virtual*/ void SbdProc::waitForFinished()
720 if ( m_sbdThread->isRunning() )
722 // kDebug() << "SbdProc::waitForFinished: waiting";
723 m_sbdThread->wait();
724 // kDebug() << "SbdProc::waitForFinished: finished waiting";
725 m_state = fsFinished;
730 * Returns the state of the Filter.
732 /*virtual*/ int SbdProc::getState() { return m_state; }
735 * Returns the filtered output.
737 /*virtual*/ QString SbdProc::getOutput() { return m_sbdThread->text(); }
740 * Acknowledges the finished filtering.
742 /*virtual*/ void SbdProc::ackFinished()
744 m_state = fsIdle;
745 m_sbdThread->setText( QString() );
749 * Stops filtering. The filteringStopped signal will emit when filtering
750 * has in fact stopped and state returns to fsIdle;
752 /*virtual*/ void SbdProc::stopFiltering()
754 if ( m_sbdThread->isRunning() )
756 m_sbdThread->terminate();
757 m_sbdThread->wait();
758 delete m_sbdThread;
759 m_sbdThread = new SbdThread(this);
760 m_sbdThread->setConfiguredSbRegExp( m_configuredRe );
761 connect( m_sbdThread, SIGNAL(filteringFinished()), this, SLOT(slotSbdThreadFilteringFinished()) );
762 m_state = fsIdle;
763 emit filteringStopped();
768 * Did this filter do anything? If the filter returns the input as output
769 * unmolested, it should return False when this method is called.
771 /*virtual*/ bool SbdProc::wasModified() { return m_sbdThread->wasModified(); }
774 * Set Sentence Boundary Regular Expression.
775 * This method will only be called if the application overrode the default.
777 * @param re The sentence delimiter regular expression.
779 /*virtual*/ void SbdProc::setSbRegExp(const QString& re) { m_sbdThread->setSbRegExp( re ); }
781 // Received when SBD Thread finishes.
782 void SbdProc::slotSbdThreadFilteringFinished()
784 m_state = fsFinished;
785 // kDebug() << "SbdProc::slotSbdThreadFilteringFinished: emitting filterFinished signal.";
786 emit filteringFinished();
789 #include "sbdproc.moc"