summaryrefslogtreecommitdiffstats
path: root/kttsd/filters
diff options
context:
space:
mode:
Diffstat (limited to 'kttsd/filters')
-rw-r--r--kttsd/filters/Makefile.am26
-rw-r--r--kttsd/filters/kttsd_filterplugin.desktop6
-rw-r--r--kttsd/filters/main.cpp140
-rw-r--r--kttsd/filters/sbd/Makefile.am27
-rw-r--r--kttsd/filters/sbd/kttsd_sbdplugin.desktop91
-rw-r--r--kttsd/filters/sbd/polish_festival_sbdrc6
-rw-r--r--kttsd/filters/sbd/sbdconf.cpp341
-rw-r--r--kttsd/filters/sbd/sbdconf.h139
-rw-r--r--kttsd/filters/sbd/sbdconfwidget.ui363
-rw-r--r--kttsd/filters/sbd/sbdplugin.cpp33
-rw-r--r--kttsd/filters/sbd/sbdproc.cpp784
-rw-r--r--kttsd/filters/sbd/sbdproc.h366
-rw-r--r--kttsd/filters/sbd/standard_sbdrc6
-rw-r--r--kttsd/filters/stringreplacer/Makefile.am34
-rw-r--r--kttsd/filters/stringreplacer/abbreviations.xml58
-rw-r--r--kttsd/filters/stringreplacer/chat-de.xml616
-rw-r--r--kttsd/filters/stringreplacer/chat.xml616
-rw-r--r--kttsd/filters/stringreplacer/editreplacementwidget.ui263
-rw-r--r--kttsd/filters/stringreplacer/emoticons.xml304
-rw-r--r--kttsd/filters/stringreplacer/festival_unspeakable_chars.xml94
-rw-r--r--kttsd/filters/stringreplacer/kmail.xml31
-rw-r--r--kttsd/filters/stringreplacer/kttsd_stringreplacerplugin.desktop92
-rw-r--r--kttsd/filters/stringreplacer/polish_festival_fixes.xml12
-rw-r--r--kttsd/filters/stringreplacer/polish_festival_unspeakables.xml78
-rw-r--r--kttsd/filters/stringreplacer/qt2plaintext.xml359
-rw-r--r--kttsd/filters/stringreplacer/stringreplacerconf.cpp725
-rw-r--r--kttsd/filters/stringreplacer/stringreplacerconf.h158
-rw-r--r--kttsd/filters/stringreplacer/stringreplacerconfwidget.ui474
-rw-r--r--kttsd/filters/stringreplacer/stringreplacerplugin.cpp32
-rw-r--r--kttsd/filters/stringreplacer/stringreplacerproc.cpp231
-rw-r--r--kttsd/filters/stringreplacer/stringreplacerproc.h97
-rw-r--r--kttsd/filters/talkerchooser/Makefile.am25
-rw-r--r--kttsd/filters/talkerchooser/female_notifications_rc10
-rw-r--r--kttsd/filters/talkerchooser/kttsd_talkerchooserplugin.desktop89
-rw-r--r--kttsd/filters/talkerchooser/talkerchooserconf.cpp269
-rw-r--r--kttsd/filters/talkerchooser/talkerchooserconf.h134
-rw-r--r--kttsd/filters/talkerchooser/talkerchooserconfwidget.ui319
-rw-r--r--kttsd/filters/talkerchooser/talkerchooserplugin.cpp32
-rw-r--r--kttsd/filters/talkerchooser/talkerchooserproc.cpp148
-rw-r--r--kttsd/filters/talkerchooser/talkerchooserproc.h87
-rw-r--r--kttsd/filters/xhtml2ssml/Doxyfile266
-rw-r--r--kttsd/filters/xhtml2ssml/demonstration.html5
-rw-r--r--kttsd/filters/xhtml2ssml/main.cpp22
-rw-r--r--kttsd/filters/xhtml2ssml/tagmappingrc5
-rw-r--r--kttsd/filters/xhtml2ssml/xhtml2ssml.cpp105
-rw-r--r--kttsd/filters/xhtml2ssml/xhtml2ssml.h61
-rw-r--r--kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop112
-rw-r--r--kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop.pcsbin0 -> 14766 bytes
-rw-r--r--kttsd/filters/xhtml2ssml/xhtml2ssml.kdevses38
-rw-r--r--kttsd/filters/xhtml2ssml/xhtml2ssml.pro10
-rw-r--r--kttsd/filters/xhtml2ssml/xmlelement.cpp102
-rw-r--r--kttsd/filters/xhtml2ssml/xmlelement.h91
-rw-r--r--kttsd/filters/xmltransformer/Makefile.am24
-rw-r--r--kttsd/filters/xmltransformer/kttsd_xmltransformerplugin.desktop88
-rw-r--r--kttsd/filters/xmltransformer/xhtml2ssml.xsl252
-rw-r--r--kttsd/filters/xmltransformer/xhtml2ssml_simple.xsl95
-rw-r--r--kttsd/filters/xmltransformer/xmltransformerconf.cpp182
-rw-r--r--kttsd/filters/xmltransformer/xmltransformerconf.h120
-rw-r--r--kttsd/filters/xmltransformer/xmltransformerconfwidget.ui249
-rw-r--r--kttsd/filters/xmltransformer/xmltransformerplugin.cpp31
-rw-r--r--kttsd/filters/xmltransformer/xmltransformerproc.cpp385
-rw-r--r--kttsd/filters/xmltransformer/xmltransformerproc.h167
62 files changed, 10125 insertions, 0 deletions
diff --git a/kttsd/filters/Makefile.am b/kttsd/filters/Makefile.am
new file mode 100644
index 0000000..8c068f0
--- /dev/null
+++ b/kttsd/filters/Makefile.am
@@ -0,0 +1,26 @@
+# Include paths. INCLUDES is maintained by KDevelop, AM_CPPFLAGS is the preferred variable,
+# so keep them synchronized.
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd \
+ -I$(kde_includes)/arts \
+ $(CFLAGS_GST) \
+ $(all_includes)
+
+SUBDIRS = stringreplacer xmltransformer sbd talkerchooser
+
+#########################################################################
+# APPLICATION SECTION
+#########################################################################
+# This is the program that gets installed. It's name is used for all
+# of the other Makefile.am variables.
+check_PROGRAMS = testfilter
+
+testfilter_SOURCES = main.cpp
+
+testfilter_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+testfilter_LDADD = \
+ $(top_builddir)/kttsd/libkttsd/libkttsd.la \
+ $(LIB_KDECORE)
+
+kde_servicetypes_DATA = kttsd_filterplugin.desktop
+
diff --git a/kttsd/filters/kttsd_filterplugin.desktop b/kttsd/filters/kttsd_filterplugin.desktop
new file mode 100644
index 0000000..afede99
--- /dev/null
+++ b/kttsd/filters/kttsd_filterplugin.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=KTTSD/FilterPlugin
+
+[PropertyDef::X-KDE-Languages]
+Type=QStringList
diff --git a/kttsd/filters/main.cpp b/kttsd/filters/main.cpp
new file mode 100644
index 0000000..d358da8
--- /dev/null
+++ b/kttsd/filters/main.cpp
@@ -0,0 +1,140 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ KTTSD Filter Test Program
+ -------------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#include <qstring.h>
+#include <iostream>
+using namespace std;
+
+#include <qtextstream.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+
+#include "filterproc.h"
+#include "talkercode.h"
+
+static const KCmdLineOptions options[] =
+{
+ { "+pluginName", I18N_NOOP("Name of a KTTSD filter plugin (required)"), 0 },
+ { "t", 0, 0 },
+ { "talker <talker>", I18N_NOOP("Talker code passed to filter"), "en" },
+ { "a", 0, 0 },
+ { "appid <appID>", I18N_NOOP("DCOP application ID passed to filter"), "testfilter" },
+ { "g", 0, 0 },
+ { "group <filterID>",
+ I18N_NOOP2("A string that appears in a single config file, not a group of config files",
+ "Config file group name passed to filter"), "testfilter" },
+ { "list", I18N_NOOP("Display list of available Filter PlugIns and exit"), 0 },
+ { "b", 0, 0 },
+ { "break", I18N_NOOP("Display tabs as \\t, otherwise they are removed"), 0 },
+ { "list", I18N_NOOP("Display list of available filter plugins and exit"), 0 },
+ KCmdLineLastOption
+};
+
+int main(int argc, char *argv[])
+{
+ KAboutData aboutdata(
+ "testfilter", I18N_NOOP("testfilter"),
+ "0.1.0", I18N_NOOP("A utility for testing KTTSD filter plugins."),
+ KAboutData::License_GPL, "(C) 2005, Gary Cramblitt <garycramblitt@comcast.net>");
+ aboutdata.addAuthor("Gary Cramblitt", I18N_NOOP("Maintainer"),"garycramblitt@comcast.net");
+
+ KCmdLineArgs::init( argc, argv, &aboutdata );
+ // Tell which options are supported
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KApplication app( false, false );
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KTrader::OfferList offers = KTrader::self()->query("KTTSD/FilterPlugin");
+
+ if (args->isSet("list"))
+ {
+ // Iterate thru the offers to list the plugins.
+ const int offersCount = offers.count();
+ for(int ndx=0; ndx < offersCount ; ++ndx)
+ {
+ QString name = offers[ndx]->name();
+ cout << name.latin1() << endl;
+ }
+ return 0;
+ }
+
+ QString filterName;
+ if (args->count() > 0) filterName = args->arg(0);
+ QString talker = args->getOption("talker");
+ QCString appId = args->getOption("appid");
+ QString groupName = args->getOption("group");
+
+ if (filterName.isEmpty()) kdError(1) << "No filter name given." << endl;
+
+ const int offersCount = offers.count();
+ for(int ndx=0; ndx < offersCount ; ++ndx)
+ {
+ if(offers[ndx]->name() == filterName)
+ {
+ // When the entry is found, load the plug in
+ // First create a factory for the library
+ KLibFactory *factory = KLibLoader::self()->factory(offers[ndx]->library().latin1());
+ if(factory)
+ {
+ // If the factory is created successfully, instantiate the KttsFilterConf class for the
+ // specific plug in to get the plug in configuration object.
+ int errorNo;
+ KttsFilterProc *plugIn =
+ KParts::ComponentFactory::createInstanceFromLibrary<KttsFilterProc>(
+ offers[ndx]->library().latin1(), NULL, offers[ndx]->library().latin1(),
+ QStringList(), &errorNo);
+ if(plugIn)
+ {
+ KConfig* config = new KConfig("kttsdrc");
+ config->setGroup( "General" );
+ plugIn->init( config, groupName );
+ QTextStream inp ( stdin, IO_ReadOnly );
+ QString text;
+ text = inp.read();
+ TalkerCode* talkerCode = new TalkerCode( talker );
+ text = plugIn->convert( text, talkerCode, appId );
+ if ( args->isSet("break") )
+ text.replace( "\t", "\\t" );
+ else
+ text.replace( "\t", "" );
+ cout << text.latin1() << endl;
+ delete config;
+ delete plugIn;
+ return 0;
+ } else
+ kdError(2) << "Unable to create instance from library." << endl;
+ } else
+ kdError(3) << "Unable to create factory." << endl;
+ }
+ }
+ kdError(4) << "Unable to find a plugin named " << filterName << endl;
+}
diff --git a/kttsd/filters/sbd/Makefile.am b/kttsd/filters/sbd/Makefile.am
new file mode 100644
index 0000000..4faa8a9
--- /dev/null
+++ b/kttsd/filters/sbd/Makefile.am
@@ -0,0 +1,27 @@
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkttsd_sbdplugin.la
+
+libkttsd_sbdplugin_la_SOURCES = \
+ sbdproc.cpp \
+ sbdconf.cpp \
+ sbdconfwidget.ui \
+ sbdplugin.cpp
+
+libkttsd_sbdplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkttsd_sbdplugin_la_LIBADD = $(top_builddir)/kttsd/libkttsd/libkttsd.la
+
+services_DATA = kttsd_sbdplugin.desktop
+servicesdir = $(kde_servicesdir)
+
+# Install data files.
+sbddatadir = $(kde_datadir)/kttsd/sbd/
+sbddata_DATA = \
+ standard_sbdrc \
+ polish_festival_sbdrc
+
+noinst_HEADERS = sbdproc.h
diff --git a/kttsd/filters/sbd/kttsd_sbdplugin.desktop b/kttsd/filters/sbd/kttsd_sbdplugin.desktop
new file mode 100644
index 0000000..0e8a483
--- /dev/null
+++ b/kttsd/filters/sbd/kttsd_sbdplugin.desktop
@@ -0,0 +1,91 @@
+[Desktop Entry]
+Name=Sentence Boundary Detector
+Name[bg]=Детектор на изречения
+Name[ca]=Detector de límit de frase
+Name[cs]=Zjištění vět
+Name[da]=Grænsedetektor for sætninger
+Name[de]=Satzgrenzenerkennung
+Name[el]=Ανιχνευτής ορίων πρότασης
+Name[es]=Detector de final de frase
+Name[et]=Lausepiiri tuvastaja
+Name[eu]=Esaldien mugen detektorea
+Name[fa]=آشکارساز کرانۀ جمله
+Name[fi]=Lauserajojen tunnistin
+Name[fr]=Détecteur de fins de phrases
+Name[ga]=Brathadóir Teorainneacha Abairtí
+Name[gl]=Detector de Límite de Frases
+Name[hu]=Mondathatár-detektáló
+Name[is]=Setningamarka greinir
+Name[it]=Rilevatore del limite delle frasi
+Name[ja]=センテンスの境界検出
+Name[ka]=წინადადების საზღვრის დამდგენი
+Name[km]= ឧបករណ៍​ចាប់​ព្រំដែន​ប្រយោគ
+Name[lt]=Sakinio ribų aptikiklis
+Name[mk]=Детектор на граница на реченица
+Name[ms]=Pengesan Sempadan Ayat
+Name[nb]=Finner setningsgrenser
+Name[nds]=Satzgrenz-Faststellen
+Name[ne]=वाक्य सीमा संसूचक
+Name[nl]=Zingrensdetectie
+Name[pl]=Wykrywanie granicy zdania
+Name[pt]=Detector de Limite de Frase
+Name[pt_BR]=Detector de Fim de Sentenças
+Name[ru]=Определитель границ предложения
+Name[sk]=Detekcia okraja vety
+Name[sl]=Detektor meje stavkov
+Name[sr]=Детектор граница реченице
+Name[sr@Latn]=Detektor granica rečenice
+Name[sv]=Meningsgränsdetektering
+Name[ta]=வாக்கிய பவுண்டரி கண்டுபிடிப்பான்
+Name[tg]=Муайянсозии ҳудуди ҷумлаҳо
+Name[tr]=Cümle Sınırlama Algılayıcı
+Name[uk]=Виявлення меж речень
+Name[vi]=Trình nhận dạng Ranh giới Câu văn
+Name[zh_CN]=语句分界检测器
+Name[zh_TW]=句子界限偵測器
+Comment=Sentence Boundary Detection Filter Plugin for KTTS
+Comment[bg]=Приставка филтър за определяне границите на изречението
+Comment[ca]=Connector del filtre detector de límit de frase pel KTTS
+Comment[cs]=Modul filtru zjišťování vět KTTS
+Comment[da]=Grænsedetektor for sætninger, filter-plugin for KTTS
+Comment[de]=Generisches KTTS-Modul zur Erkennung von Satzgrenzen
+Comment[el]=Φίλτρο πρόσθετο ανίχνευσης ορίων πρότασης για το KTTS
+Comment[es]=Complemento de filtrado que detecta el final de las frases para KTTS
+Comment[et]=KTTS-i lausepiiri tuvastamise filtri plugin
+Comment[eu]=KTTS-ren esaldien mugen detektorearen iragazkien plugina
+Comment[fa]=وصلۀ پالایۀ آشکارسازی کرانۀ جمله برای KTTS
+Comment[fi]=Lauserajojen tunnistussuodatin liitännäinen KTTS-ohjelmalle
+Comment[fr]=Module de filtrage de fins de phrases pour KTTS
+Comment[gl]=Plugin de Filtro Detector de Límite de Frases para KTTS
+Comment[hu]=Mondatvég-detektáló szűrőmodul a KTTS-hez
+Comment[is]=Setningamarka greinasía fyrir KTTS
+Comment[it]=Plugin per il filtro di rilevazione del limite delle frasi per KTTS
+Comment[ja]=KTTS 用センテンスの境界検出フィルタプラグイン
+Comment[ka]=წინადადების საზღვრის დამდგენის ფილტრის მოდული KTTS-სთვის
+Comment[km]=កម្មវិធី​ជំនួយ​តម្រង​ឧបករណ៍​ចាប់​ព្រំដែន​ប្រយោគ​សម្រាប់ KTTS
+Comment[lt]=Sakinio ribų aptikiklio filtro priedas, skirtas KTTS
+Comment[mk]=Филтер за KTTS за детекција на границата на реченица
+Comment[ms]=Plugin Penapis Pengesan Sempadan Ayat bagi KTTS
+Comment[nb]=Programtillegg for KTTS som finner setningsgrenser
+Comment[nds]=KTTS-Filtermoduul för dat Faststellen vun Satzgrenzen
+Comment[ne]=KTTS का लागि वाक्य सीमा पत्ता लगाउने फिल्टर
+Comment[nl]=Zingrensdetectieplugin voor KTTS
+Comment[pl]=Wtyczka filtra wykrywania granicy zdania dla KTTS
+Comment[pt]='Plugin' de Filtro de Detecção de Limite de Frase para o KTTS
+Comment[pt_BR]=Plug-in de Detector de Fim de Sentenças para o KTTSD
+Comment[ru]=Фильтр границ предложения для KTTS
+Comment[sk]=Modul filtra na detekciu okraja vety v KTTS
+Comment[sl]=Filtrni vstavek KTTS za zanavanje mej stavka
+Comment[sr]=Филтерски прикључак KTTS-а за откривање граница реченице
+Comment[sr@Latn]=Filterski priključak KTTS-a za otkrivanje granica rečenice
+Comment[sv]=Insticksprogram för KTTS med filter för meningsgränsdetektering
+Comment[ta]=KTTSக்கான வாக்கிய கண்டறி அலங்கார சொருகுப்பொருள்
+Comment[tg]=Филтри ҳудуди ҷумлаҳо барои KTTS
+Comment[tr]=KTTS için Cümle Sınırlama Algılayıcı Süzgeci Eklentisi
+Comment[uk]=Втулок фільтра виявлення меж для KTTS
+Comment[vi]=Trình bổ sung Lọc Nhận dạng Ranh giới Câu văn cho KTTS
+Comment[zh_TW]=KTTS 使用的偵測句子是否已結束的外掛程式
+Type=Service
+ServiceTypes=KTTSD/FilterPlugin
+X-KDE-Library=libkttsd_sbdplugin
+X-KDE-Languages=en,en_US,en_GB,en_CA,es,es_mx,cy,de,fi,cs,pl
diff --git a/kttsd/filters/sbd/polish_festival_sbdrc b/kttsd/filters/sbd/polish_festival_sbdrc
new file mode 100644
index 0000000..ad64153
--- /dev/null
+++ b/kttsd/filters/sbd/polish_festival_sbdrc
@@ -0,0 +1,6 @@
+[Filter]
+AppID=
+LanguageCodes=pl
+SentenceBoundary=\\t
+SentenceDelimiterRegExp=([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))
+UserFilterName=Polish Sentence Boundary Detector
diff --git a/kttsd/filters/sbd/sbdconf.cpp b/kttsd/filters/sbd/sbdconf.cpp
new file mode 100644
index 0000000..2d4cf5d
--- /dev/null
+++ b/kttsd/filters/sbd/sbdconf.cpp
@@ -0,0 +1,341 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Sentence Boundary Detection Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qradiobutton.h>
+
+// KDE includes.
+#include <kglobal.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+#include <kpushbutton.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kregexpeditorinterface.h>
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+#include <kfiledialog.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// SBD includes.
+#include "sbdconf.h"
+#include "sbdconf.moc"
+
+/**
+* Constructor
+*/
+SbdConf::SbdConf( QWidget *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterConf(parent, name)
+{
+ // kdDebug() << "SbdConf::SbdConf: Running" << endl;
+
+ // Create configuration widget.
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint(), "SbdConfigWidgetLayout");
+ layout->setAlignment (Qt::AlignTop);
+ m_widget = new SbdConfWidget(this, "SbdConfigWidget");
+ layout->addWidget(m_widget);
+
+ // Determine if kdeutils Regular Expression Editor is installed.
+ m_reEditorInstalled = !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty();
+
+ m_widget->reButton->setEnabled( m_reEditorInstalled );
+ if ( m_reEditorInstalled )
+ connect( m_widget->reButton, SIGNAL(clicked()), this, SLOT(slotReButton_clicked()) );
+
+ connect( m_widget->reLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->sbLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->nameLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->appIdLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect(m_widget->languageBrowseButton, SIGNAL(clicked()),
+ this, SLOT(slotLanguageBrowseButton_clicked()));
+ connect(m_widget->loadButton, SIGNAL(clicked()),
+ this, SLOT(slotLoadButton_clicked()));
+ connect(m_widget->saveButton, SIGNAL(clicked()),
+ this, SLOT(slotSaveButton_clicked()));
+ connect(m_widget->clearButton, SIGNAL(clicked()),
+ this, SLOT(slotClearButton_clicked()));
+
+ // Set up defaults.
+ defaults();
+}
+
+/**
+* Destructor.
+*/
+SbdConf::~SbdConf(){
+ // kdDebug() << "SbdConf::~SbdConf: Running" << endl;
+}
+
+/**
+* This method is invoked whenever the module should read its
+* configuration (most of the times from a config file) and update the
+* user interface. This happens when the user clicks the "Reset" button in
+* the control center, to undo all of his changes and restore the currently
+* valid settings. Note that kttsmgr calls this when the plugin is
+* loaded, so it not necessary to call it in your constructor.
+* The plugin should read its configuration from the specified group
+* in the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* loading your configuration.
+*/
+void SbdConf::load(KConfig* config, const QString& configGroup){
+ // kdDebug() << "SbdConf::load: Running" << endl;
+ config->setGroup( configGroup );
+ m_widget->nameLineEdit->setText(
+ config->readEntry("UserFilterName", m_widget->nameLineEdit->text()) );
+ m_widget->reLineEdit->setText(
+ config->readEntry("SentenceDelimiterRegExp", m_widget->reLineEdit->text()) );
+ m_widget->sbLineEdit->setText(
+ config->readEntry("SentenceBoundary", m_widget->sbLineEdit->text()) );
+ QStringList langCodeList = config->readListEntry("LanguageCodes");
+ if (!langCodeList.isEmpty())
+ m_languageCodeList = langCodeList;
+ QString language = "";
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx)
+ {
+ if (!language.isEmpty()) language += ",";
+ language += KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[ndx]);
+ }
+ m_widget->languageLineEdit->setText(language);
+ m_widget->appIdLineEdit->setText(
+ config->readEntry("AppID", m_widget->appIdLineEdit->text()) );
+}
+
+/**
+* This function gets called when the user wants to save the settings in
+* the user interface, updating the config files or wherever the
+* configuration is stored. The method is called when the user clicks "Apply"
+* or "Ok". The plugin should save its configuration in the specified
+* group of the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* saving your configuration.
+*/
+void SbdConf::save(KConfig* config, const QString& configGroup){
+ // kdDebug() << "SbdConf::save: Running" << endl;
+ config->setGroup( configGroup );
+ config->writeEntry("UserFilterName", m_widget->nameLineEdit->text() );
+ config->writeEntry("SentenceDelimiterRegExp", m_widget->reLineEdit->text() );
+ config->writeEntry("SentenceBoundary", m_widget->sbLineEdit->text() );
+ config->writeEntry("LanguageCodes", m_languageCodeList );
+ config->writeEntry("AppID", m_widget->appIdLineEdit->text().replace(" ", "") );
+}
+
+/**
+* This function is called to set the settings in the module to sensible
+* default values. It gets called when hitting the "Default" button. The
+* default values should probably be the same as the ones the application
+* uses when started without a config file. Note that defaults should
+* be applied to the on-screen widgets; not to the config file.
+*/
+void SbdConf::defaults(){
+ // kdDebug() << "SbdConf::defaults: Running" << endl;
+ m_widget->nameLineEdit->setText( i18n("Standard Sentence Boundary Detector") );
+ m_widget->reLineEdit->setText( "([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))" );
+ m_widget->sbLineEdit->setText( "\\1\\t" );
+ m_languageCodeList.clear();
+ m_widget->languageLineEdit->setText( "" );
+ m_widget->appIdLineEdit->setText( "" );
+ // kdDebug() << "SbdConf::defaults: Exiting" << endl;
+}
+
+/**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+bool SbdConf::supportsMultiInstance() { return true; }
+
+/**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+QString SbdConf::userPlugInName()
+{
+ if ( m_widget->reLineEdit->text().isEmpty() )
+ return QString::null;
+ else
+ return m_widget->nameLineEdit->text();
+}
+
+/**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * @return True if this filter is a SBD.
+ */
+bool SbdConf::isSBD() { return true; }
+
+void SbdConf::slotReButton_clicked()
+{
+ // Show Regular Expression Editor dialog if it is installed.
+ if ( !m_reEditorInstalled ) return;
+ QDialog *editorDialog =
+ KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+ if ( editorDialog )
+ {
+ // kdeutils was installed, so the dialog was found. Fetch the editor interface.
+ KRegExpEditorInterface *reEditor =
+ static_cast<KRegExpEditorInterface *>(editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT( reEditor ); // This should not fail!// now use the editor.
+ reEditor->setRegExp( m_widget->reLineEdit->text() );
+ int dlgResult = editorDialog->exec();
+ if ( dlgResult == QDialog::Accepted )
+ {
+ QString re = reEditor->regExp();
+ m_widget->reLineEdit->setText( re );
+ configChanged();
+ }
+ delete editorDialog;
+ } else return;
+}
+
+void SbdConf::slotLanguageBrowseButton_clicked()
+{
+ // Create a QHBox to host KListView.
+ QHBox* hBox = new QHBox(m_widget, "SelectLanguage_hbox");
+ // Create a KListView and fill with all known languages.
+ KListView* langLView = new KListView(hBox, "SelectLanguage_lview");
+ langLView->addColumn(i18n("Language"));
+ langLView->addColumn(i18n("Code"));
+ langLView->setSelectionMode(QListView::Extended);
+ QStringList allLocales = KGlobal::locale()->allLanguagesTwoAlpha();
+ QString locale;
+ QString languageCode;
+ QString countryCode;
+ QString charSet;
+ QString language;
+ // Blank line so user can select no language.
+ QListViewItem* item = new KListViewItem(langLView, "", "");
+ if (m_languageCodeList.isEmpty()) item->setSelected(true);
+ const int allLocalesCount = allLocales.count();
+ for (int ndx=0; ndx < allLocalesCount; ++ndx)
+ {
+ locale = allLocales[ndx];
+ KGlobal::locale()->splitLocale(locale, languageCode, countryCode, charSet);
+ language = KGlobal::locale()->twoAlphaToLanguageName(languageCode);
+ if (!countryCode.isEmpty()) language +=
+ " (" + KGlobal::locale()->twoAlphaToCountryName(countryCode)+")";
+ QListViewItem* item = new KListViewItem(langLView, language, locale);
+ if (m_languageCodeList.contains(locale)) item->setSelected(true);
+ }
+ // Sort by language.
+ langLView->setSorting(0);
+ langLView->sort();
+ // Display the box in a dialog.
+ KDialogBase* dlg = new KDialogBase(
+ KDialogBase::Swallow,
+ i18n("Select Languages"),
+ KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ m_widget,
+ "SelectLanguage_dlg",
+ true,
+ true);
+ dlg->setMainWidget(hBox);
+ dlg->setHelp("", "kttsd");
+ dlg->setInitialSize(QSize(300, 500), false);
+ int dlgResult = dlg->exec();
+ languageCode = QString::null;
+ if (dlgResult == QDialog::Accepted)
+ {
+ m_languageCodeList.clear();
+ QListViewItem* item = langLView->firstChild();
+ while (item)
+ {
+ if (item->isSelected()) m_languageCodeList += item->text(1);
+ item = item->nextSibling();
+ }
+ }
+ delete dlg;
+ // TODO: Also delete KListView and QHBox?
+ if (dlgResult != QDialog::Accepted) return;
+ language = "";
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx)
+ {
+ if (!language.isEmpty()) language += ",";
+ language += KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[ndx]);
+ }
+ m_widget->languageLineEdit->setText(language);
+ configChanged();
+}
+
+void SbdConf::slotLoadButton_clicked()
+{
+ // QString dataDir = KGlobal::dirs()->resourceDirs("data").last() + "/kttsd/stringreplacer/";
+ QString dataDir = KGlobal::dirs()->findAllResources("data", "kttsd/sbd/").last();
+ QString filename = KFileDialog::getOpenFileName(
+ dataDir,
+ "*rc|SBD Config (*rc)",
+ m_widget,
+ "sbd_loadfile");
+ if ( filename.isEmpty() ) return;
+ KConfig* cfg = new KConfig( filename, true, false, 0 );
+ load( cfg, "Filter" );
+ delete cfg;
+ configChanged();
+}
+
+void SbdConf::slotSaveButton_clicked()
+{
+ QString filename = KFileDialog::getSaveFileName(
+ KGlobal::dirs()->saveLocation( "data" ,"kttsd/sbd/", false ),
+ "*rc|SBD Config (*rc)",
+ m_widget,
+ "sbd_savefile");
+ if ( filename.isEmpty() ) return;
+ KConfig* cfg = new KConfig( filename, false, false, 0 );
+ save( cfg, "Filter" );
+ delete cfg;
+}
+
+void SbdConf::slotClearButton_clicked()
+{
+ m_widget->nameLineEdit->setText( QString::null );
+ m_widget->reLineEdit->setText( QString::null );
+ m_widget->sbLineEdit->setText( QString::null );
+ m_languageCodeList.clear();
+ m_widget->languageLineEdit->setText( QString::null );
+ m_widget->appIdLineEdit->setText( QString::null );
+ configChanged();
+}
diff --git a/kttsd/filters/sbd/sbdconf.h b/kttsd/filters/sbd/sbdconf.h
new file mode 100644
index 0000000..131a759
--- /dev/null
+++ b/kttsd/filters/sbd/sbdconf.h
@@ -0,0 +1,139 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Standard Sentence Boundary Detection Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _SBDCONF_H_
+#define _SBDCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// SBD includes.
+#include "sbdconfwidget.h"
+
+class KDialogBase;
+class EditReplacementWidget;
+
+class SbdConf : public KttsFilterConf
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ SbdConf( QWidget *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor
+ */
+ virtual ~SbdConf();
+
+ /**
+ * This method is invoked whenever the module should read its
+ * configuration (most of the times from a config file) and update the
+ * user interface. This happens when the user clicks the "Reset" button in
+ * the control center, to undo all of his changes and restore the currently
+ * valid settings. Note that KTTSMGR calls this when the plugin is
+ * loaded, so it not necessary to call it in your constructor.
+ * The plugin should read its configuration from the specified group
+ * in the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * loading your configuration.
+ *
+ * When a plugin is first added to KTTSMGR, @e load will be called with
+ * a Null @e configGroup. In this case, the plugin will not have
+ * any instance-specific parameters to load, but it may still wish
+ * to load parameters that apply to all instances of the plugin.
+ */
+ virtual void load(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function gets called when the user wants to save the settings in
+ * the user interface, updating the config files or wherever the
+ * configuration is stored. The method is called when the user clicks "Apply"
+ * or "Ok". The plugin should save its configuration in the specified
+ * group of the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * saving your configuration.
+ */
+ virtual void save(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function is called to set the settings in the module to sensible
+ * default values. It gets called when hitting the "Default" button. The
+ * default values should probably be the same as the ones the application
+ * uses when started without a config file. Note that defaults should
+ * be applied to the on-screen widgets; not to the config file.
+ */
+ virtual void defaults();
+
+ /**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+ virtual bool supportsMultiInstance();
+
+ /**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+ virtual QString userPlugInName();
+
+ /**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * @return True if this filter is a SBD.
+ */
+ virtual bool isSBD();
+
+ private slots:
+ void slotReButton_clicked();
+ void slotLanguageBrowseButton_clicked();
+ void slotLoadButton_clicked();
+ void slotSaveButton_clicked();
+ void slotClearButton_clicked();
+
+ private:
+
+ // Configuration Widget.
+ SbdConfWidget* m_widget;
+ // True if kdeutils Regular Expression Editor is installed.
+ bool m_reEditorInstalled;
+ // Language Code.
+ QStringList m_languageCodeList;
+};
+
+#endif //_SBDCONF_H_
diff --git a/kttsd/filters/sbd/sbdconfwidget.ui b/kttsd/filters/sbd/sbdconfwidget.ui
new file mode 100644
index 0000000..17cd525
--- /dev/null
+++ b/kttsd/filters/sbd/sbdconfwidget.ui
@@ -0,0 +1,363 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>SbdConfWidget</class>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SbdConfWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>544</width>
+ <height>315</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;WARNING: This filter is a key component of the KTTS system. Please read the KTTS Handbook before modifying these settings.&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The name of this filter. Enter any descriptive name you like.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="text">
+ <string>Standard Sentence Boundary Detector</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The name of this filter. Enter any descriptive name you like.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>reLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Sentence boundary regular expression:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The regular expression that detects boundaries between sentences in text jobs.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>reLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The regular expression that detects boundaries between sentences in text jobs.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>reButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout17_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>sbLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Replacement sentence boundary:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This string replaces the matched regular expression. &lt;b&gt;Important&lt;/b&gt;: must end with tab (\t).</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sbLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This string replaces the matched regular expression. &lt;b&gt;Important&lt;/b&gt;: must end with tab (\t).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="4" column="0">
+ <property name="name">
+ <cstring>applyGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Apply This &amp;Filter When</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These settings determines when the filter is applied to text.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>languageLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Language is:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank the filter applies to all text jobs of any language.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>appIdLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Application &amp;ID contains:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank the filter applies to all text jobs of any language.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>languageBrowseButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to select one or more languages. This filter will be applied to text jobs of those languages.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. Use &lt;b&gt;knotify&lt;/b&gt; to match all messages sent as KDE notifications. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="5" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>loadButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Load...</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to load a Sentence Boundary Detection configuration from a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>saveButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Sa&amp;ve...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to save this Sentence Boundary Detection configuration to a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>clearButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Clea&amp;r</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to clear everything.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>nameLineEdit</tabstop>
+ <tabstop>reLineEdit</tabstop>
+ <tabstop>reButton</tabstop>
+ <tabstop>sbLineEdit</tabstop>
+ <tabstop>languageLineEdit</tabstop>
+ <tabstop>languageBrowseButton</tabstop>
+ <tabstop>appIdLineEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/filters/sbd/sbdplugin.cpp b/kttsd/filters/sbd/sbdplugin.cpp
new file mode 100644
index 0000000..ab14080
--- /dev/null
+++ b/kttsd/filters/sbd/sbdplugin.cpp
@@ -0,0 +1,33 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generating the factories so Sentence Boundary Detection Filter can be used
+ as plug in.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#include <kgenericfactory.h>
+
+#include "sbdconf.h"
+#include "sbdproc.h"
+
+typedef K_TYPELIST_2( SbdProc, SbdConf ) SbdPlugin;
+K_EXPORT_COMPONENT_FACTORY( libkttsd_sbdplugin,
+ KGenericFactory<SbdPlugin>("kttsd_sbd") )
+
diff --git a/kttsd/filters/sbd/sbdproc.cpp b/kttsd/filters/sbd/sbdproc.cpp
new file mode 100644
index 0000000..cdc80d9
--- /dev/null
+++ b/kttsd/filters/sbd/sbdproc.cpp
@@ -0,0 +1,784 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Sentence Boundary Detection Filter class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qregexp.h>
+#include <qdom.h>
+#include <qapplication.h>
+
+// KDE includes.
+#include <kdebug.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+// KTTS includes.
+#include "utils.h"
+#include "talkercode.h"
+
+// SdbProc includes.
+#include "sbdproc.h"
+
+/**
+ * Constructor.
+ */
+SbdThread::SbdThread( QObject *parent, const char *name ) :
+ QObject( parent, name ),
+ QThread()
+{
+}
+
+/**
+ * Destructor.
+ */
+/*virtual*/ SbdThread::~SbdThread()
+{
+}
+
+/**
+ * Get/Set text being processed.
+ */
+void SbdThread::setText( const QString& text ) { m_text = text; }
+QString SbdThread::text() { return m_text; }
+
+/**
+ * Set/Get TalkerCode.
+ */
+void SbdThread::setTalkerCode( TalkerCode* talkerCode ) { m_talkerCode = talkerCode; }
+TalkerCode* SbdThread::talkerCode() { return m_talkerCode; }
+
+/**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+void SbdThread::setSbRegExp( const QString& re ) { m_re = re; }
+
+/**
+ * The configured Sentence Boundary Regular Expression.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+void SbdThread::setConfiguredSbRegExp( const QString& re ) { m_configuredRe = re; }
+
+/**
+ * The configured Sentence Boundary that replaces SB regular expression.
+ *
+ * @param sb The sentence boundary replacement.
+ *
+ */
+void SbdThread::setConfiguredSentenceBoundary( const QString& sb ) { m_configuredSentenceBoundary = sb; }
+
+/**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+void SbdThread::setWasModified(bool wasModified) { m_wasModified = wasModified; }
+bool SbdThread::wasModified() { return m_wasModified; }
+
+// Given a tag name, returns SsmlElemType.
+SbdThread::SsmlElemType SbdThread::tagToSsmlElemType( const QString tagName )
+{
+ if ( tagName == "speak" ) return etSpeak;
+ if ( tagName == "voice" ) return etVoice;
+ if ( tagName == "prosody" ) return etProsody;
+ if ( tagName == "emphasis" ) return etEmphasis;
+ if ( tagName == "break" ) return etBreak;
+ if ( tagName == "s" ) return etPS;
+ if ( tagName == "p" ) return etPS;
+ return etNotSsml;
+}
+
+// Parses an SSML element, pushing current settings onto the context stack.
+void SbdThread::pushSsmlElem( SsmlElemType et, const QDomElement& elem )
+{
+ // TODO: Need to convert relative values into absolute values and also convert
+ // only to values recognized by SSML2SABLE stylesheet. Either that or enhance all
+ // the synth stylesheets.
+ QDomNamedNodeMap attrList = elem.attributes();
+ int attrCount = attrList.count();
+ switch ( et )
+ {
+ case etSpeak: {
+ SpeakElem e = m_speakStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "lang" ) e.lang = a.value();
+ }
+ m_speakStack.push( e );
+ break; }
+ case etVoice: {
+ VoiceElem e = m_voiceStack.top();
+ // TODO: Since Festival chokes on <voice> tags, don't output them at all.
+ // This means we can't support voice changes, and probably more irritatingly,
+ // gender changes either.
+ m_voiceStack.push( e );
+ break; }
+ case etProsody: {
+ ProsodyElem e = m_prosodyStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "pitch" ) e.pitch = a.value();
+ if ( a.name() == "contour" ) e.contour = a.value();
+ if ( a.name() == "range" ) e.range = a.value();
+ if ( a.name() == "rate" ) e.rate = a.value();
+ if ( a.name() == "duration" ) e.duration = a.value();
+ if ( a.name() == "volume" ) e.volume = a.value();
+ }
+ m_prosodyStack.push( e );
+ break; }
+ case etEmphasis: {
+ EmphasisElem e = m_emphasisStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "level" ) e.level = a.value();
+ }
+ m_emphasisStack.push( e );
+ break; }
+ case etPS: {
+ PSElem e = m_psStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "lang" ) e.lang = a.value();
+ }
+ m_psStack.push( e );
+ break; }
+ default: break;
+ }
+}
+
+// Given an attribute name and value, constructs an XML representation of the attribute,
+// i.e., name="value".
+QString SbdThread::makeAttr( const QString& name, const QString& value )
+{
+ if ( value.isEmpty() ) return QString::null;
+ return " " + name + "=\"" + value + "\"";
+}
+
+// Returns an XML representation of an SSML tag from the top of the context stack.
+QString SbdThread::makeSsmlElem( SsmlElemType et )
+{
+ QString s;
+ QString a;
+ switch ( et )
+ {
+ // Must always output speak tag, otherwise kttsd won't think each sentence is SSML.
+ // For all other tags, only output the tag if it contains at least one attribute.
+ case etSpeak: {
+ SpeakElem e = m_speakStack.top();
+ s = "<speak";
+ if ( !e.lang.isEmpty() ) s += makeAttr( "lang", e.lang );
+ s += ">";
+ break; }
+ case etVoice: {
+ VoiceElem e = m_voiceStack.top();
+ a += makeAttr( "lang", e.lang );
+ a += makeAttr( "gender", e.gender );
+ a += makeAttr( "age", QString::number(e.age) );
+ a += makeAttr( "name", e.name );
+ a += makeAttr( "variant", e.variant );
+ if ( !a.isEmpty() ) s = "<voice" + a + ">";
+ break; }
+ case etProsody: {
+ ProsodyElem e = m_prosodyStack.top();
+ a += makeAttr( "pitch", e.pitch );
+ a += makeAttr( "contour", e.contour );
+ a += makeAttr( "range", e.range );
+ a += makeAttr( "rate", e.rate );
+ a += makeAttr( "duration", e.duration );
+ a += makeAttr( "volume", e.volume );
+ if ( !a.isEmpty() ) s = "<prosody" + a + ">";
+ break; }
+ case etEmphasis: {
+ EmphasisElem e = m_emphasisStack.top();
+ a += makeAttr( "level", e.level );
+ if ( !a.isEmpty() ) s = "<emphasis" + a + ">";
+ break; }
+ case etPS: {
+ break; }
+ default: break;
+ }
+ return s;
+}
+
+// Pops element from the indicated context stack.
+void SbdThread::popSsmlElem( SsmlElemType et )
+{
+ switch ( et )
+ {
+ case etSpeak: m_speakStack.pop(); break;
+ case etVoice: m_voiceStack.pop(); break;
+ case etProsody: m_prosodyStack.pop(); break;
+ case etEmphasis: m_emphasisStack.pop(); break;
+ case etPS: m_psStack.pop(); break;
+ default: break;
+ }
+}
+
+// Returns an XML representation of a break element.
+QString SbdThread::makeBreakElem( const QDomElement& e )
+{
+ QString s = "<break";
+ QDomNamedNodeMap attrList = e.attributes();
+ int attrCount = attrList.count();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ s += makeAttr( a.name(), a.value() );
+ }
+ s += ">";
+ return s;
+}
+
+// Converts a text fragment into a CDATA section.
+QString SbdThread::makeCDATA( const QString& text )
+{
+ QString s = "<![CDATA[";
+ s += text;
+ s += "]]>";
+ return s;
+}
+
+// Returns an XML representation of an utterance node consisting of voice,
+// prosody, and emphasis elements.
+QString SbdThread::makeSentence( const QString& text )
+{
+ QString s;
+ QString v = makeSsmlElem( etVoice );
+ QString p = makeSsmlElem( etProsody );
+ QString e = makeSsmlElem( etEmphasis );
+ // TODO: Lang settings from psStack.
+ if ( !v.isEmpty() ) s += v;
+ if ( !p.isEmpty() ) s += p;
+ if ( !e.isEmpty() ) s += e;
+ // Escape ampersands and less thans.
+ QString newText = text;
+ newText.replace(QRegExp("&(?!amp;)"), "&amp;");
+ newText.replace(QRegExp("<(?!lt;)"), "&lt;");
+ s += newText;
+ if ( !e.isEmpty() ) s += "</emphasis>";
+ if ( !p.isEmpty() ) s += "</prosody>";
+ if ( !v.isEmpty() ) s += "</voice>";
+ return s;
+}
+
+// Starts a sentence by returning a speak tag.
+QString SbdThread::startSentence()
+{
+ if ( m_sentenceStarted ) return QString::null;
+ QString s;
+ s += makeSsmlElem( etSpeak );
+ m_sentenceStarted = true;
+ return s;
+}
+
+// Ends a sentence and appends a Tab.
+QString SbdThread::endSentence()
+{
+ if ( !m_sentenceStarted ) return QString::null;
+ QString s = "</speak>";
+ s += "\t";
+ m_sentenceStarted = false;
+ return s;
+}
+
+// Parses a node of the SSML tree and recursively parses its children.
+// Returns the filtered text with each sentence a complete ssml tree.
+QString SbdThread::parseSsmlNode( QDomNode& n, const QString& re )
+{
+ QString result;
+ switch ( n.nodeType() )
+ {
+ case QDomNode::ElementNode: { // = 1
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+ SsmlElemType et = tagToSsmlElemType( tagName );
+ switch ( et )
+ {
+ case etSpeak:
+ case etVoice:
+ case etProsody:
+ case etEmphasis:
+ case etPS:
+ {
+ pushSsmlElem( et, e );
+ QDomNode t = n.firstChild();
+ while ( !t.isNull() )
+ {
+ result += parseSsmlNode( t, re );
+ t = t.nextSibling();
+ }
+ popSsmlElem( et );
+ if ( et == etPS )
+ result += endSentence();
+ break;
+ }
+ case etBreak:
+ {
+ // Break elements are empty.
+ result += makeBreakElem( e );
+ }
+ // Ignore any elements we don't recognize.
+ default: break;
+ }
+ break; }
+ case QDomNode::AttributeNode: { // = 2
+ break; }
+ case QDomNode::TextNode: { // = 3
+ QString s = parsePlainText( n.toText().data(), re );
+ // QString d = s;
+ // d.replace("\t", "\\t");
+ // kdDebug() << "SbdThread::parseSsmlNode: parsedPlainText = [" << d << "]" << endl;
+ QStringList sentenceList = QStringList::split( '\t', s, false );
+ int lastNdx = sentenceList.count() - 1;
+ for ( int ndx=0; ndx < lastNdx; ++ndx )
+ {
+ result += startSentence();
+ result += makeSentence( sentenceList[ndx] );
+ result += endSentence();
+ }
+ // Only output sentence boundary if last text fragment ended a sentence.
+ if ( lastNdx >= 0 )
+ {
+ result += startSentence();
+ result += makeSentence( sentenceList[lastNdx] );
+ if ( s.endsWith( "\t" ) ) result += endSentence();
+ }
+ break; }
+ case QDomNode::CDATASectionNode: { // = 4
+ QString s = parsePlainText( n.toCDATASection().data(), re );
+ QStringList sentenceList = QStringList::split( '\t', s, false );
+ int lastNdx = sentenceList.count() - 1;
+ for ( int ndx=0; ndx < lastNdx; ++ndx )
+ {
+ result += startSentence();
+ result += makeSentence( makeCDATA( sentenceList[ndx] ) );
+ result += endSentence();
+ }
+ // Only output sentence boundary if last text fragment ended a sentence.
+ if ( lastNdx >= 0 )
+ {
+ result += startSentence();
+ result += makeSentence( makeCDATA( sentenceList[lastNdx] ) );
+ if ( s.endsWith( "\t" ) ) result += endSentence();
+ }
+ break; }
+ case QDomNode::EntityReferenceNode: { // = 5
+ break; }
+ case QDomNode::EntityNode: { // = 6
+ break; }
+ case QDomNode::ProcessingInstructionNode: { // = 7
+ break; }
+ case QDomNode::CommentNode: { // = 8
+ break; }
+ case QDomNode::DocumentNode: { // = 9
+ break; }
+ case QDomNode::DocumentTypeNode: { // = 10
+ break; }
+ case QDomNode::DocumentFragmentNode: { // = 11
+ break; }
+ case QDomNode::NotationNode: { // = 12
+ break; }
+ case QDomNode::BaseNode: { // = 21
+ break; }
+ case QDomNode::CharacterDataNode: { // = 22
+ break; }
+ }
+ return result;
+}
+
+// Parses Ssml.
+QString SbdThread::parseSsml( const QString& inputText, const QString& re )
+{
+ QRegExp sentenceDelimiter = QRegExp( re );
+
+ // Read the text into xml dom tree.
+ QDomDocument doc( "" );
+ // If an error occurs parsing the SSML, return "invalid S S M L".
+ if ( !doc.setContent( inputText ) ) return i18n("Invalid S S M L.");
+
+ // Set up context stacks and set defaults for all element attributes.
+ m_speakStack.clear();
+ m_voiceStack.clear();
+ m_prosodyStack.clear();
+ m_emphasisStack.clear();
+ m_psStack.clear();
+ SpeakElem se = { "" };
+ m_speakStack.push ( se );
+ VoiceElem ve = {"", "neutral", 40, "", ""};
+ m_voiceStack.push( ve );
+ ProsodyElem pe = { "medium", "", "medium", "medium", "", "medium" };
+ m_prosodyStack.push( pe );
+ EmphasisElem em = { "" };
+ m_emphasisStack.push( em );
+ PSElem pse = { "" };
+ m_psStack.push ( pse );
+
+ // This flag is used to close out a previous sentence.
+ m_sentenceStarted = false;
+
+ // Get the root element (speak) and recursively process its children.
+ QDomElement docElem = doc.documentElement();
+ QDomNode n = docElem.firstChild();
+ QString ssml = parseSsmlNode( docElem, re );
+
+ // Close out last sentence.
+ if ( m_sentenceStarted ) ssml += "</speak>";
+
+ return ssml;
+}
+
+// Parses code. Each newline is converted into a tab character (\t).
+QString SbdThread::parseCode( const QString& inputText )
+{
+ QString temp = inputText;
+ // Replace newlines with tabs.
+ temp.replace("\n","\t");
+ // Remove leading spaces.
+ temp.replace(QRegExp("\\t +"), "\t");
+ // Remove trailing spaces.
+ temp.replace(QRegExp(" +\\t"), "\t");
+ // Remove blank lines.
+ temp.replace(QRegExp("\t\t+"),"\t");
+ return temp;
+}
+
+// Parses plain text.
+QString SbdThread::parsePlainText( const QString& inputText, const QString& re )
+{
+ // kdDebug() << "SbdThread::parsePlainText: parsing " << inputText << " with re " << re << endl;
+ QRegExp sentenceDelimiter = QRegExp( re );
+ QString temp = inputText;
+ // Replace sentence delimiters with tab.
+ temp.replace(sentenceDelimiter, m_configuredSentenceBoundary);
+ // Replace remaining newlines with spaces.
+ temp.replace("\n"," ");
+ temp.replace("\r"," ");
+ // Remove leading spaces.
+ temp.replace(QRegExp("\\t +"), "\t");
+ // Remove trailing spaces.
+ temp.replace(QRegExp(" +\\t"), "\t");
+ // Remove blank lines.
+ temp.replace(QRegExp("\t\t+"),"\t");
+ return temp;
+}
+
+// This is where the real work takes place.
+/*virtual*/ void SbdThread::run()
+{
+ // kdDebug() << "SbdThread::run: processing text = " << m_text << endl;
+
+ // TODO: Determine if we should do anything or not.
+ m_wasModified = true;
+
+ // Determine what kind of input text we are dealing with.
+ int textType;
+ if ( KttsUtils::hasRootElement( m_text, "speak" ) )
+ textType = ttSsml;
+ else
+ {
+ // Examine just the first 500 chars to see if it is code.
+ QString p = m_text.left( 500 );
+ if ( p.contains( QRegExp( "(/\\*)|(if\\b\\()|(^#include\\b)" ) ) )
+ textType = ttCode;
+ else
+ textType = ttPlain;
+ }
+
+ // If application specified a sentence delimiter regular expression, use that,
+ // otherwise use configured default.
+ QString re = m_re;
+ if ( re.isEmpty() ) re = m_configuredRe;
+
+ // Replace spaces, tabs, and formfeeds with a single space.
+ m_text.replace(QRegExp("[ \\t\\f]+"), " ");
+
+ // Perform the filtering based on type of text.
+ switch ( textType )
+ {
+ case ttSsml:
+ m_text = parseSsml( m_text, re );
+ break;
+
+ case ttCode:
+ m_text = parseCode( m_text );
+ break;
+
+ case ttPlain:
+ m_text = parsePlainText( m_text, re);
+ break;
+ }
+
+ // Clear app-specified sentence delimiter. App must call setSbRegExp for each conversion.
+ m_re = QString::null;
+
+ // kdDebug() << "SbdThread::run: filtered text = " << m_text << endl;
+
+ // Result is in m_text;
+
+ // Post an event. We need to emit filterFinished signal, but not from the
+ // separate thread.
+ QCustomEvent* ev = new QCustomEvent(QEvent::User + 301);
+ QApplication::postEvent(this, ev);
+}
+
+bool SbdThread::event ( QEvent * e )
+{
+ if ( e->type() == (QEvent::User + 301) )
+ {
+ // kdDebug() << "SbdThread::event: emitting filteringFinished signal." << endl;
+ emit filteringFinished();
+ return true;
+ }
+ else return false;
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * Constructor.
+ */
+SbdProc::SbdProc( QObject *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterProc(parent, name)
+{
+ // kdDebug() << "SbdProc::SbdProc: Running" << endl;
+ m_sbdThread = new SbdThread( parent, *name + "_thread" );
+ connect( m_sbdThread, SIGNAL(filteringFinished()), this, SLOT(slotSbdThreadFilteringFinished()) );
+}
+
+/**
+ * Destructor.
+ */
+SbdProc::~SbdProc()
+{
+ // kdDebug() << "SbdProc::~SbdProc: Running" << endl;
+ if ( m_sbdThread )
+ {
+ if ( m_sbdThread->running() )
+ {
+ m_sbdThread->terminate();
+ m_sbdThread->wait();
+ }
+ delete m_sbdThread;
+ }
+}
+
+/**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+bool SbdProc::init(KConfig* config, const QString& configGroup){
+ // kdDebug() << "PlugInProc::init: Running" << endl;
+ config->setGroup( configGroup );
+// m_configuredRe = config->readEntry( "SentenceDelimiterRegExp", "([\\.\\?\\!\\:\\;])\\s|(\\n *\\n)" );
+ m_configuredRe = config->readEntry( "SentenceDelimiterRegExp", "([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))" );
+ m_sbdThread->setConfiguredSbRegExp( m_configuredRe );
+ QString sb = config->readEntry( "SentenceBoundary", "\\1\t" );
+ sb.replace( "\\t", "\t" );
+ m_sbdThread->setConfiguredSentenceBoundary( sb );
+ m_appIdList = config->readListEntry( "AppID" );
+ m_languageCodeList = config->readListEntry( "LanguageCodes" );
+ return true;
+}
+
+/**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * If so, the filter should implement @ref setSbRegExp() .
+ * @return True if this filter is a SBD.
+ */
+/*virtual*/ bool SbdProc::isSBD() { return true; }
+
+/**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+/*virtual*/ bool SbdProc::supportsAsync() { return true; }
+
+/**
+ * Convert input, returning output. Runs synchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+/*virtual*/ QString SbdProc::convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId)
+{
+ if ( asyncConvert( inputText, talkerCode, appId) )
+ {
+ waitForFinished();
+ // kdDebug() << "SbdProc::convert: returning " << getOutput() << endl;
+ return getOutput();
+ } else return inputText;
+}
+
+/**
+ * Convert input. Runs asynchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ * @return False if the filter cannot perform the conversion.
+ *
+ * When conversion is completed, emits signal @ref filteringFinished. Calling
+ * program may then call @ref getOutput to retrieve converted text. Calling
+ * program must call @ref ackFinished to acknowledge the conversion.
+ */
+/*virtual*/ bool SbdProc::asyncConvert(const QString& inputText, TalkerCode* talkerCode,
+ const QCString& appId)
+{
+ m_sbdThread->setWasModified( false );
+ // If language doesn't match, return input unmolested.
+ if ( !m_languageCodeList.isEmpty() )
+ {
+ QString languageCode = talkerCode->languageCode();
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText <<
+ // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
+ if ( !m_languageCodeList.contains( languageCode ) )
+ {
+ if ( !talkerCode->countryCode().isEmpty() )
+ {
+ languageCode += '_' + talkerCode->countryCode();
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText <<
+ // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
+ if ( !m_languageCodeList.contains( languageCode ) ) return false;
+ } else return false;
+ }
+ }
+ // If appId doesn't match, return input unmolested.
+ if ( !m_appIdList.isEmpty() )
+ {
+ // kdDebug() << "SbdProc::convert: converting " << inputText << " if appId "
+ // << appId << " matches " << m_appIdList << endl;
+ bool found = false;
+ QString appIdStr = appId;
+ for ( uint ndx=0; ndx < m_appIdList.count(); ++ndx )
+ {
+ if ( appIdStr.contains(m_appIdList[ndx]) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found ) return false;
+ }
+ m_sbdThread->setText( inputText );
+ m_sbdThread->setTalkerCode( talkerCode );
+ m_state = fsFiltering;
+ m_sbdThread->start();
+ return true;
+}
+
+/**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+/*virtual*/ void SbdProc::waitForFinished()
+{
+ if ( m_sbdThread->running() )
+ {
+ // kdDebug() << "SbdProc::waitForFinished: waiting" << endl;
+ m_sbdThread->wait();
+ // kdDebug() << "SbdProc::waitForFinished: finished waiting" << endl;
+ m_state = fsFinished;
+ }
+}
+
+/**
+ * Returns the state of the Filter.
+ */
+/*virtual*/ int SbdProc::getState() { return m_state; }
+
+/**
+ * Returns the filtered output.
+ */
+/*virtual*/ QString SbdProc::getOutput() { return m_sbdThread->text(); }
+
+/**
+ * Acknowledges the finished filtering.
+ */
+/*virtual*/ void SbdProc::ackFinished()
+{
+ m_state = fsIdle;
+ m_sbdThread->setText( QString::null );
+}
+
+/**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+/*virtual*/ void SbdProc::stopFiltering()
+{
+ if ( m_sbdThread->running() )
+ {
+ m_sbdThread->terminate();
+ m_sbdThread->wait();
+ delete m_sbdThread;
+ m_sbdThread = new SbdThread();
+ m_sbdThread->setConfiguredSbRegExp( m_configuredRe );
+ connect( m_sbdThread, SIGNAL(filteringFinished()), this, SLOT(slotSbdThreadFilteringFinished()) );
+ m_state = fsIdle;
+ emit filteringStopped();
+ }
+}
+
+/**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+/*virtual*/ bool SbdProc::wasModified() { return m_sbdThread->wasModified(); }
+
+/**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+/*virtual*/ void SbdProc::setSbRegExp(const QString& re) { m_sbdThread->setSbRegExp( re ); }
+
+// Received when SBD Thread finishes.
+void SbdProc::slotSbdThreadFilteringFinished()
+{
+ m_state = fsFinished;
+ // kdDebug() << "SbdProc::slotSbdThreadFilteringFinished: emitting filterFinished signal." << endl;
+ emit filteringFinished();
+}
+
+#include "sbdproc.moc"
diff --git a/kttsd/filters/sbd/sbdproc.h b/kttsd/filters/sbd/sbdproc.h
new file mode 100644
index 0000000..6c181d5
--- /dev/null
+++ b/kttsd/filters/sbd/sbdproc.h
@@ -0,0 +1,366 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Sentence Boundary Detection (SBD) Filter class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+/******************************************************************************
+
+ This class performs three kinds of SBD:
+ 1. If the text is SSML, generates new SSML where the prosody and voice
+ tags are fully specified for each sentence. This allows user to
+ advance or rewind by sentence without losing SSML context.
+ Input is considered to be SSML if the top-level element is a
+ <speak> tag.
+ 2. If the text is code, each line is considered to be a sentence.
+ Input is considered to be code if any of the following strings are
+ detected:
+ slash asterisk
+ if left-paren
+ pound include
+ 3. If the text is plain text, performs SBD using specified Regular
+ Expression.
+
+ Text is output with tab characters (\t) separating sentences.
+
+ ******************************************************************************/
+
+#ifndef _SBDPROC_H_
+#define _SBDPROC_H_
+
+// Qt includes.
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qthread.h>
+#include <qvaluestack.h>
+#include <qevent.h>
+
+// KTTS includes.
+#include "filterproc.h"
+
+class TalkerCode;
+class KConfig;
+class QDomElement;
+class QDomNode;
+
+class SbdThread: public QObject, public QThread
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ */
+ SbdThread( QObject *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor.
+ */
+ virtual ~SbdThread();
+
+ /**
+ * Get/Set text being processed.
+ */
+ void setText( const QString& text );
+ QString text();
+
+ /**
+ * Set/Get TalkerCode.
+ */
+ void setTalkerCode( TalkerCode* talkerCode );
+ TalkerCode* talkerCode();
+
+ /**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+ void setSbRegExp( const QString& re );
+
+ /**
+ * The configured Sentence Boundary Regular Expression.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+ void setConfiguredSbRegExp( const QString& re );
+
+ /**
+ * The configured Sentence Boundary that replaces SB regular expression.
+ *
+ * @param sb The sentence boundary replacement.
+ *
+ */
+ void setConfiguredSentenceBoundary( const QString& sb );
+
+ /**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+ void setWasModified(bool wasModified);
+ bool wasModified();
+
+ signals:
+ void filteringFinished();
+
+ protected:
+ virtual void run();
+ virtual bool event ( QEvent * e );
+
+ private:
+ enum TextType {
+ ttSsml, // SSML
+ ttCode, // Code
+ ttPlain // Plain text
+ };
+
+ enum SsmlElemType {
+ etSpeak,
+ etVoice,
+ etProsody,
+ etEmphasis,
+ etPS, // Paragraph or sentence (we don't care).
+ etBreak,
+ etNotSsml
+ };
+
+ // Speak Element.
+ struct SpeakElem {
+ QString lang; // xml:lang="en".
+ };
+
+ // Voice Element.
+ struct VoiceElem {
+ QString lang; // xml:lang="en".
+ QString gender; // "male", "female", or "neutral".
+ uint age; // Age in years.
+ QString name; // Synth-specific voice name.
+ QString variant; // Ignored.
+ };
+
+ // Prosody Element.
+ struct ProsodyElem {
+ QString pitch; // "x-low", "low", "medium", "high", "x-high", "default".
+ QString contour; // Pitch contour (ignored).
+ QString range; // "x-low", "low", "medium", "high", "x-high", "default".
+ QString rate; // "x-slow", "slow", "medium", "fast", "x-fast", "default".
+ QString duration; // Ignored.
+ QString volume; // "silent", "x-soft", "soft", "medium", "load", "x-load", "default".
+ };
+
+ // Emphasis Element.
+ struct EmphasisElem {
+ QString level; // "strong", "moderate", "none" and "reduced"
+ };
+
+ // Break Element.
+ struct BreakElem {
+ QString strength; // "x-weak", "weak", "medium" (default value), "strong",
+ // or "x-strong", "none"
+ QString time; // Ignored.
+ };
+
+ // Paragraph and Sentence Elements.
+ struct PSElem {
+ QString lang; // xml:lang="en".
+ };
+
+ // Given a tag name, returns SsmlElemType.
+ SsmlElemType tagToSsmlElemType(const QString tagName);
+ // Parses an SSML element, pushing current settings onto the context stack.
+ void pushSsmlElem( SsmlElemType et, const QDomElement& elem );
+ // Given an attribute name and value, constructs an XML representation of the attribute,
+ // i.e., name="value".
+ QString makeAttr( const QString& name, const QString& value );
+ // Returns an XML representation of an SSML tag from the top of the context stack.
+ QString makeSsmlElem( SsmlElemType et );
+ // Pops element from the indicated context stack.
+ void popSsmlElem( SsmlElemType et );
+ QString makeBreakElem( const QDomElement& e );
+ // Converts a text fragment into a CDATA section.
+ QString makeCDATA( const QString& text );
+ // Returns an XML representation of an utterance node consisting of voice,
+ // prosody, and emphasis elements.
+ QString makeSentence( const QString& text );
+ // Starts a sentence by returning a speak tag.
+ QString startSentence();
+ // Ends a sentence and appends a Tab.
+ QString endSentence();
+ // Parses a node of the SSML tree and recursively parses its children.
+ // Returns the filtered text with each sentence a complete ssml tree.
+ QString parseSsmlNode( QDomNode& n, const QString& re );
+
+ // Parses Ssml.
+ QString parseSsml( const QString& inputText, const QString& re );
+ // Parses code. Each newline is converted into a tab character (\t).
+ QString parseCode( const QString& inputText );
+ // Parses plain text.
+ QString parsePlainText( const QString& inputText, const QString& re );
+
+ // Context stacks.
+ QValueStack<SpeakElem> m_speakStack;
+ QValueStack<VoiceElem> m_voiceStack;
+ QValueStack<ProsodyElem> m_prosodyStack;
+ QValueStack<EmphasisElem> m_emphasisStack;
+ QValueStack<PSElem> m_psStack;
+
+ // The text being processed.
+ QString m_text;
+ // Talker Code.
+ TalkerCode* m_talkerCode;
+ // Configured default Sentence Delimiter regular expression.
+ QString m_configuredRe;
+ // Configured Sentence Boundary replacement expression.
+ QString m_configuredSentenceBoundary;
+ // Application-specified Sentence Delimiter regular expression (if any).
+ QString m_re;
+ // False if input was not modified.
+ bool m_wasModified;
+ // True when a sentence has been started.
+ bool m_sentenceStarted;
+};
+
+class SbdProc : virtual public KttsFilterProc
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ */
+ SbdProc( QObject *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor.
+ */
+ virtual ~SbdProc();
+
+ /**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+ virtual bool init( KConfig *config, const QString &configGroup );
+
+ /**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * If so, the filter should implement @ref setSbRegExp() .
+ * @return True if this filter is a SBD.
+ */
+ virtual bool isSBD();
+
+ /**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+ virtual bool supportsAsync();
+
+ /**
+ * Convert input, returning output. Runs synchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+ virtual QString convert( const QString& inputText, TalkerCode* talkerCode, const QCString& appId );
+
+ /**
+ * Convert input. Runs asynchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ * @return False if the filter cannot perform the conversion.
+ *
+ * When conversion is completed, emits signal @ref filteringFinished. Calling
+ * program may then call @ref getOutput to retrieve converted text. Calling
+ * program must call @ref ackFinished to acknowledge the conversion.
+ */
+ virtual bool asyncConvert( const QString& inputText, TalkerCode* talkerCode, const QCString& appId );
+
+ /**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+ virtual void waitForFinished();
+
+ /**
+ * Returns the state of the Filter.
+ */
+ virtual int getState();
+
+ /**
+ * Returns the filtered output.
+ */
+ virtual QString getOutput();
+
+ /**
+ * Acknowledges the finished filtering.
+ */
+ virtual void ackFinished();
+
+ /**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+ virtual void stopFiltering();
+
+ /**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+ virtual bool wasModified();
+
+ /**
+ * Set Sentence Boundary Regular Expression.
+ */
+ virtual void setSbRegExp( const QString& re );
+
+ private slots:
+ // Received when SBD Thread finishes.
+ void slotSbdThreadFilteringFinished();
+
+ private:
+ // If not empty, apply filters only to apps using talkers speaking these language codes.
+ QStringList m_languageCodeList;
+ // If not empty, apply filter only to apps containing this string.
+ QStringList m_appIdList;
+ // SBD Thread Object.
+ SbdThread* m_sbdThread;
+ // State.
+ int m_state;
+ // Configured default Sentence Delimiter regular expression.
+ QString m_configuredRe;
+};
+
+#endif // _SBDPROC_H_
diff --git a/kttsd/filters/sbd/standard_sbdrc b/kttsd/filters/sbd/standard_sbdrc
new file mode 100644
index 0000000..76037a5
--- /dev/null
+++ b/kttsd/filters/sbd/standard_sbdrc
@@ -0,0 +1,6 @@
+[Filter]
+AppID=
+LanguageCodes=
+SentenceBoundary=\\1\\t
+SentenceDelimiterRegExp=([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))
+UserFilterName=Standard Sentence Boundary Detector
diff --git a/kttsd/filters/stringreplacer/Makefile.am b/kttsd/filters/stringreplacer/Makefile.am
new file mode 100644
index 0000000..b52085c
--- /dev/null
+++ b/kttsd/filters/stringreplacer/Makefile.am
@@ -0,0 +1,34 @@
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkttsd_stringreplacerplugin.la
+
+libkttsd_stringreplacerplugin_la_SOURCES = \
+ stringreplacerconfwidget.ui \
+ editreplacementwidget.ui \
+ stringreplacerconf.cpp \
+ stringreplacerproc.cpp \
+ stringreplacerplugin.cpp
+libkttsd_stringreplacerplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkttsd_stringreplacerplugin_la_LIBADD = $(top_builddir)/kttsd/libkttsd/libkttsd.la
+
+services_DATA = kttsd_stringreplacerplugin.desktop
+servicesdir = $(kde_servicesdir)
+
+# Install data files.
+stringreplacerdatadir = $(kde_datadir)/kttsd/stringreplacer/
+stringreplacerdata_DATA = \
+ abbreviations.xml \
+ chat.xml \
+ chat-de.xml \
+ emoticons.xml \
+ polish_festival_fixes.xml \
+ polish_festival_unspeakables.xml \
+ kmail.xml \
+ festival_unspeakable_chars.xml \
+ qt2plaintext.xml
+
+noinst_HEADERS = stringreplacerconfwidget.h editreplacementwidget.h
diff --git a/kttsd/filters/stringreplacer/abbreviations.xml b/kttsd/filters/stringreplacer/abbreviations.xml
new file mode 100644
index 0000000..d04b7aa
--- /dev/null
+++ b/kttsd/filters/stringreplacer/abbreviations.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wordlist>
+ <name>Abbreviations</name>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[TODO]]></match>
+ <subst><![CDATA[To Do]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[CPU]]></match>
+ <subst><![CDATA[C P U]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[DCOP]]></match>
+ <subst><![CDATA[D Cop]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[IP]]></match>
+ <subst><![CDATA[I P]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[KDE]]></match>
+ <subst><![CDATA[K D E]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[KTTS]]></match>
+ <subst><![CDATA[K T T S]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[kttsd]]></match>
+ <subst><![CDATA[K T T S D]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[KTTSD]]></match>
+ <subst><![CDATA[K T T S D]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[TTS]]></match>
+ <subst><![CDATA[T T S]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/chat-de.xml b/kttsd/filters/stringreplacer/chat-de.xml
new file mode 100644
index 0000000..801c144
--- /dev/null
+++ b/kttsd/filters/stringreplacer/chat-de.xml
@@ -0,0 +1,616 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wordlist>
+ <name>Internet Relay Chat and IM (Deutsch)</name>
+ <language-code>de</language-code>
+ <appid>knotify</appid>
+ <appid>kopete</appid>
+ <appid>kvirc</appid>
+ <appid>ksirc</appid>
+ <appid>konversation</appid>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<qt>]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[&lt([^>]+)&gt]]></match>
+ <subst><![CDATA[ \1 meint ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<br>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</qt>]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\;\)]]></match>
+ <subst><![CDATA[ winkt]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\<]]></match>
+ <subst><![CDATA[ traurig]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\/]]></match>
+ <subst><![CDATA[stirnrunzeln]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\>]]></match>
+ <subst><![CDATA[ grinst]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\|]]></match>
+ <subst><![CDATA[ernsthaft]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\-\|]]></match>
+ <subst><![CDATA[ernsthaft]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\-\)]]></match>
+ <subst><![CDATA[ laechelt]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\'\(]]></match>
+ <subst><![CDATA[weint]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\(]]></match>
+ <subst><![CDATA[ traurig]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\)]]></match>
+ <subst><![CDATA[ laechelt]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\@]]></match>
+ <subst><![CDATA[ boese]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\$]]></match>
+ <subst><![CDATA[ schaemt sich]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\@\)]]></match>
+ <subst><![CDATA[ katzengesicht]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(6\)]]></match>
+ <subst><![CDATA[ teufelsfratze]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(a\)]]></match>
+ <subst><![CDATA[ engelsgesicht]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:D]]></match>
+ <subst><![CDATA[ grins]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(h\)]]></match>
+ <subst><![CDATA[ kuhl]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\so\_O]]></match>
+ <subst><![CDATA[ winkt]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:p]]></match>
+ <subst><![CDATA[ baeh]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(s\)]]></match>
+ <subst><![CDATA[ gaehn]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ad]]></match>
+ <subst><![CDATA[fuege hinzu]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[afaik]]></match>
+ <subst><![CDATA[soviel ich weiss]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[afk]]></match>
+ <subst><![CDATA[nicht an tastatur]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[asap]]></match>
+ <subst><![CDATA[so schnell wie moeglich]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[atm]]></match>
+ <subst><![CDATA[momentan]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ayt]]></match>
+ <subst><![CDATA[bist du da]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[b4]]></match>
+ <subst><![CDATA[vorher]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[b4n]]></match>
+ <subst><![CDATA[tschuess]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[bak]]></match>
+ <subst><![CDATA[zurueck]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[bbl]]></match>
+ <subst><![CDATA[komme spaeter wieder]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[bf]]></match>
+ <subst><![CDATA[freund]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[b/f]]></match>
+ <subst><![CDATA[freund]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[brb]]></match>
+ <subst><![CDATA[bin gleich wieder da]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[btw]]></match>
+ <subst><![CDATA[nebenbei bemerkt]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cu]]></match>
+ <subst><![CDATA[bis dann]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cul]]></match>
+ <subst><![CDATA[bis spaeter]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cul8r]]></match>
+ <subst><![CDATA[bis spaeter]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cya]]></match>
+ <subst><![CDATA[bis spaeter]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[d/l]]></match>
+ <subst><![CDATA[daunlohd]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[dvd]]></match>
+ <subst><![CDATA[die wie die]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[f2f]]></match>
+ <subst><![CDATA[von angesicht zu angesicht]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[fubar]]></match>
+ <subst><![CDATA[leuft trotz reparatur nicht]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[fyi]]></match>
+ <subst><![CDATA[zu deiner information]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[gf]]></match>
+ <subst><![CDATA[freundin]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[g/f]]></match>
+ <subst><![CDATA[freundin]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[gtg]]></match>
+ <subst><![CDATA[ich muss weck]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[hmm]]></match>
+ <subst><![CDATA[aehm]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[hth]]></match>
+ <subst><![CDATA[hoffe es hilft]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ic]]></match>
+ <subst><![CDATA[verstehe]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[iirc]]></match>
+ <subst><![CDATA[wenn ich recht erinnere]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imao]]></match>
+ <subst><![CDATA[meiner arroganten meinung nach]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imco]]></match>
+ <subst><![CDATA[meiner wohlueberlegten meinung nach]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ime]]></match>
+ <subst><![CDATA[meiner erfahrung nach]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imho]]></match>
+ <subst><![CDATA[meiner bescheidenen meinung nach]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imnsho]]></match>
+ <subst><![CDATA[meiner unbescheidenen meinung nach]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imo]]></match>
+ <subst><![CDATA[meiner meinung nach]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ip]]></match>
+ <subst><![CDATA[ei pie]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[isdn]]></match>
+ <subst><![CDATA[i es de en]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[k]]></match>
+ <subst><![CDATA[ho key]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[kde]]></match>
+ <subst><![CDATA[ka de eh]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[kbd]]></match>
+ <subst><![CDATA[tastatur]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[lshmba]]></match>
+ <subst><![CDATA[halte mir den bauch vor lachen]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[lthtt]]></match>
+ <subst><![CDATA[unbeschreibliches gelaechter]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ltns]]></match>
+ <subst><![CDATA[lange nicht gesehen]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[lol]]></match>
+ <subst><![CDATA[h h h]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[m8]]></match>
+ <subst><![CDATA[kumpel]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[msg]]></match>
+ <subst><![CDATA[nachricht]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[np]]></match>
+ <subst><![CDATA[kein problem]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[oic]]></match>
+ <subst><![CDATA[oh verstehe]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ok]]></match>
+ <subst><![CDATA[ho key]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ott]]></match>
+ <subst><![CDATA[übertreiben]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[pc]]></match>
+ <subst><![CDATA[pie cie]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ppc]]></match>
+ <subst><![CDATA[power pie cie]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ppl]]></match>
+ <subst><![CDATA[leute]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[rotfl]]></match>
+ <subst><![CDATA[kugelt sich vor lachen]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[rotflol]]></match>
+ <subst><![CDATA[bruellt vor lachen]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[rsvp]]></match>
+ <subst><![CDATA[antworte bitte]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[scsi]]></match>
+ <subst><![CDATA[skasi]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[suse]]></match>
+ <subst><![CDATA[suse]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[sec]]></match>
+ <subst><![CDATA[sekunde]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[secs]]></match>
+ <subst><![CDATA[sekunden]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[thx]]></match>
+ <subst><![CDATA[danke]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[tnx]]></match>
+ <subst><![CDATA[danke]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wo]]></match>
+ <subst><![CDATA[ohne]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wb]]></match>
+ <subst><![CDATA[willkommen urueck]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wtf]]></match>
+ <subst><![CDATA[was zum teufel]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wth]]></match>
+ <subst><![CDATA[was zum teufel]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wtg]]></match>
+ <subst><![CDATA[gut gemacht]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wtgp]]></match>
+ <subst><![CDATA[bitte um privatchett]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wth]]></match>
+ <subst><![CDATA[was zum teufel]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wygiswypf]]></match>
+ <subst><![CDATA[was du kriegst ist was du bezahlst]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[yw]]></match>
+ <subst><![CDATA[gruess diche]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\_]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\|]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\\o\/]]></match>
+ <subst><![CDATA[tschuess]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\/o\\]]></match>
+ <subst><![CDATA[haelt sich die ohren zu]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\:\-\/]]></match>
+ <subst><![CDATA[stirnrunzeln]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/chat.xml b/kttsd/filters/stringreplacer/chat.xml
new file mode 100644
index 0000000..2518824
--- /dev/null
+++ b/kttsd/filters/stringreplacer/chat.xml
@@ -0,0 +1,616 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wordlist>
+ <name>Internet Relay Chat and IM</name>
+ <language-code>en</language-code>
+ <appid>knotify</appid>
+ <appid>kopete</appid>
+ <appid>kvirc</appid>
+ <appid>ksirc</appid>
+ <appid>konversation</appid>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<qt>]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[&lt([^>]+)&gt]]></match>
+ <subst><![CDATA[ \1 says ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<br>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</qt>]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\;\)]]></match>
+ <subst><![CDATA[ winks]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\<]]></match>
+ <subst><![CDATA[ sad face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\/]]></match>
+ <subst><![CDATA[ frowns ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\>]]></match>
+ <subst><![CDATA[ grins]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\|]]></match>
+ <subst><![CDATA[ straight faced]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\-\|]]></match>
+ <subst><![CDATA[ straight faced]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\-\)]]></match>
+ <subst><![CDATA[ smiles]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\'\(]]></match>
+ <subst><![CDATA[ cries]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\(]]></match>
+ <subst><![CDATA[ sad face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\)]]></match>
+ <subst><![CDATA[ smiles]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\@]]></match>
+ <subst><![CDATA[ angry look]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\$]]></match>
+ <subst><![CDATA[ blushes]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\@\)]]></match>
+ <subst><![CDATA[ cat face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(6\)]]></match>
+ <subst><![CDATA[ devil]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(a\)]]></match>
+ <subst><![CDATA[ angel]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:D]]></match>
+ <subst><![CDATA[ grins]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(h\)]]></match>
+ <subst><![CDATA[ cool shades]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\so\_O]]></match>
+ <subst><![CDATA[ winks]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:p]]></match>
+ <subst><![CDATA[ sticks out tung]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(s\)]]></match>
+ <subst><![CDATA[ sleepy]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ad]]></match>
+ <subst><![CDATA[add]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[afaik]]></match>
+ <subst><![CDATA[as far as I know]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[afk]]></match>
+ <subst><![CDATA[away from keyboard]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[asap]]></match>
+ <subst><![CDATA[as soon as possable]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[atm]]></match>
+ <subst><![CDATA[at the moment]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ayt]]></match>
+ <subst><![CDATA[are you there]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[b4]]></match>
+ <subst><![CDATA[before]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[b4n]]></match>
+ <subst><![CDATA[bye for now]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[bak]]></match>
+ <subst><![CDATA[back]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[bbl]]></match>
+ <subst><![CDATA[be back later]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[bf]]></match>
+ <subst><![CDATA[boy friend]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[b/f]]></match>
+ <subst><![CDATA[boy friend]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[brb]]></match>
+ <subst><![CDATA[be right back]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[btw]]></match>
+ <subst><![CDATA[by the way]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cu]]></match>
+ <subst><![CDATA[see you]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cul]]></match>
+ <subst><![CDATA[see you later]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cul8r]]></match>
+ <subst><![CDATA[see you later]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[cya]]></match>
+ <subst><![CDATA[see you later]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[d/l]]></match>
+ <subst><![CDATA[down load]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[dvd]]></match>
+ <subst><![CDATA[de vee de]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[f2f]]></match>
+ <subst><![CDATA[face to face]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[fubar]]></match>
+ <subst><![CDATA[fecked up beyond all repair]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[fyi]]></match>
+ <subst><![CDATA[for your information]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[gf]]></match>
+ <subst><![CDATA[girl friend]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[g/f]]></match>
+ <subst><![CDATA[girl friend]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[gtg]]></match>
+ <subst><![CDATA[got to go]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[hmm]]></match>
+ <subst><![CDATA[hurrmmm]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[hth]]></match>
+ <subst><![CDATA[hope that helps]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ic]]></match>
+ <subst><![CDATA[i see]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[iirc]]></match>
+ <subst><![CDATA[if I recall correctly]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imao]]></match>
+ <subst><![CDATA[in my arrogant opinion]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imco]]></match>
+ <subst><![CDATA[in my considered opinion]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ime]]></match>
+ <subst><![CDATA[in my experience]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imho]]></match>
+ <subst><![CDATA[in my humble opinion]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imnsho]]></match>
+ <subst><![CDATA[in my not so humble opinion]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[imo]]></match>
+ <subst><![CDATA[in my opinion]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ip]]></match>
+ <subst><![CDATA[i p]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[isdn]]></match>
+ <subst><![CDATA[i s d n]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[k]]></match>
+ <subst><![CDATA[ho kay]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[kde]]></match>
+ <subst><![CDATA[k d e]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[kbd]]></match>
+ <subst><![CDATA[keyboard]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[lshmba]]></match>
+ <subst><![CDATA[laughing so hard my belly aches]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[lthtt]]></match>
+ <subst><![CDATA[laughing too hard to type]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ltns]]></match>
+ <subst><![CDATA[long time no see]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[lol]]></match>
+ <subst><![CDATA[lafes out loud]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[m8]]></match>
+ <subst><![CDATA[mate]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[msg]]></match>
+ <subst><![CDATA[message]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[np]]></match>
+ <subst><![CDATA[no problem]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[oic]]></match>
+ <subst><![CDATA[oh i see]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ok]]></match>
+ <subst><![CDATA[ho kay]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ott]]></match>
+ <subst><![CDATA[over the top]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[pc]]></match>
+ <subst><![CDATA[p c]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ppc]]></match>
+ <subst><![CDATA[power p c]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[ppl]]></match>
+ <subst><![CDATA[people]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[rotfl]]></match>
+ <subst><![CDATA[rolling on the floor laughing]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[rotflol]]></match>
+ <subst><![CDATA[rolling on the floor laughing out loud]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[rsvp]]></match>
+ <subst><![CDATA[please reply]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[scsi]]></match>
+ <subst><![CDATA[scuzy]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[suse]]></match>
+ <subst><![CDATA[sue see]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[sec]]></match>
+ <subst><![CDATA[second]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[secs]]></match>
+ <subst><![CDATA[seconds]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[thx]]></match>
+ <subst><![CDATA[thanks]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[tnx]]></match>
+ <subst><![CDATA[thanks]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wo]]></match>
+ <subst><![CDATA[without]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wb]]></match>
+ <subst><![CDATA[welcome back]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wtf]]></match>
+ <subst><![CDATA[what the heck]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wth]]></match>
+ <subst><![CDATA[what the heck]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wtg]]></match>
+ <subst><![CDATA[way to go]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wtgp]]></match>
+ <subst><![CDATA[want to go private]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wth]]></match>
+ <subst><![CDATA[what the heck]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[wygiswypf]]></match>
+ <subst><![CDATA[what you get is what you pay for]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[yw]]></match>
+ <subst><![CDATA[your welcome]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\_]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\|]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\\o\/]]></match>
+ <subst><![CDATA[ cheers ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\/o\\]]></match>
+ <subst><![CDATA[ covers ears ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\:\-\/]]></match>
+ <subst><![CDATA[ frowns ]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/editreplacementwidget.ui b/kttsd/filters/stringreplacer/editreplacementwidget.ui
new file mode 100644
index 0000000..917cdf8
--- /dev/null
+++ b/kttsd/filters/stringreplacer/editreplacementwidget.ui
@@ -0,0 +1,263 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EditReplacementWidget</class>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>EditReplacementWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>393</width>
+ <height>148</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>255</horstretch>
+ <verstretch>255</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>typeButtonGroup</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>&amp;Type</string>
+ </property>
+ <property name="exclusive">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>wordRadioButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Word</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>regexpRadioButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Regular &amp;expression</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>caseCheckBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Match &amp;case</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>substLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Replace with:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>matchLineEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>matchLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Match:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>matchLineEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>substLineEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>matchButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="0">
+ <property name="name">
+ <cstring>matchLineEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>wordRadioButton</tabstop>
+ <tabstop>regexpRadioButton</tabstop>
+ <tabstop>caseCheckBox</tabstop>
+ <tabstop>matchLineEdit</tabstop>
+ <tabstop>matchButton</tabstop>
+ <tabstop>substLineEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/filters/stringreplacer/emoticons.xml b/kttsd/filters/stringreplacer/emoticons.xml
new file mode 100644
index 0000000..f522358
--- /dev/null
+++ b/kttsd/filters/stringreplacer/emoticons.xml
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wordlist>
+ <name>Emoticons (English)</name>
+ <language-code>en</language-code>
+ <appid>knotify</appid>
+ <appid>kopete</appid>
+ <appid>ksirc</appid>
+ <appid>kvirc</appid>
+ <appid>konversation</appid>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\)]]></match>
+ <subst><![CDATA[. smiles]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\-\)]]></match>
+ <subst><![CDATA[. smiles]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\p]]></match>
+ <subst><![CDATA[. sticks out tung]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:D]]></match>
+ <subst><![CDATA[. grins]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\>]]></match>
+ <subst><![CDATA[.grins]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\;\)]]></match>
+ <subst><![CDATA[. winks]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\o\_\0]]></match>
+ <subst><![CDATA[. winks]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\(]]></match>
+ <subst><![CDATA[. sad face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\<]]></match>
+ <subst><![CDATA[. sad face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\\:\|]]></match>
+ <subst><![CDATA[. straight faced]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\-\|]]></match>
+ <subst><![CDATA[. straight faced]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\$]]></match>
+ <subst><![CDATA[. blushes]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\\(h\)]]></match>
+ <subst><![CDATA[. cool shades]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\@]]></match>
+ <subst><![CDATA[. angry look]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\'\(]]></match>
+ <subst><![CDATA[. cries]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(a\)]]></match>
+ <subst><![CDATA[. angel]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(6\)]]></match>
+ <subst><![CDATA[. devil]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\%\)]]></match>
+ <subst><![CDATA[. hand cuffs]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\:\[\]]></match>
+ <subst><![CDATA[. bat]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\@\)]]></match>
+ <subst><![CDATA[. cat face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\&\)]]></match>
+ <subst><![CDATA[. dog face]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\*\)]]></match>
+ <subst><![CDATA[. star]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(s\)]]></match>
+ <subst><![CDATA[. sleepy]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(Y\)]]></match>
+ <subst><![CDATA[. thumbs up]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(n\)]]></match>
+ <subst><![CDATA[. thumbs down]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\}\)]]></match>
+ <subst><![CDATA[. girl huggs]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\{\)]]></match>
+ <subst><![CDATA[. boy huggs]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(l\)]]></match>
+ <subst><![CDATA[. love]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(U\)]]></match>
+ <subst><![CDATA[. is hart brooken]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(k\)]]></match>
+ <subst><![CDATA[. kisses]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\^\)]]></match>
+ <subst><![CDATA[. birthday cake]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(g\)]]></match>
+ <subst><![CDATA[. a gift]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(f\)]]></match>
+ <subst><![CDATA[. flower]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(w\)]]></match>
+ <subst><![CDATA[. willted flower]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(t\)]]></match>
+ <subst><![CDATA[. telephone]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(e\)]]></match>
+ <subst><![CDATA[. e mail]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(m\)]]></match>
+ <subst><![CDATA[. messenger]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(o\)]]></match>
+ <subst><![CDATA[. time]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(x\)]]></match>
+ <subst><![CDATA[. girl]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(z\)]]></match>
+ <subst><![CDATA[. boy]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(p\)]]></match>
+ <subst><![CDATA[. picture]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\~\)]]></match>
+ <subst><![CDATA[. film]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(b\)]]></match>
+ <subst><![CDATA[. glass of beer]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(d\)]]></match>
+ <subst><![CDATA[. glass of wine]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(\#\)]]></match>
+ <subst><![CDATA[. sunny]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(r\)]]></match>
+ <subst><![CDATA[. rainbow]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(8\)]]></match>
+ <subst><![CDATA[. note]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(i\)]]></match>
+ <subst><![CDATA[. idea]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\s\(c\)]]></match>
+ <subst><![CDATA[. coffee cup]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/festival_unspeakable_chars.xml b/kttsd/filters/stringreplacer/festival_unspeakable_chars.xml
new file mode 100644
index 0000000..5086d01
--- /dev/null
+++ b/kttsd/filters/stringreplacer/festival_unspeakable_chars.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wordlist>
+ <name>Fix Festival Unspeakable Characters</name>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x80]]></match>
+ <subst><![CDATA[ Euro ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x82]]></match>
+ <subst><![CDATA[']]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x84]]></match>
+ <subst><![CDATA["]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x85]]></match>
+ <subst><![CDATA[...]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x89]]></match>
+ <subst><![CDATA[ per Mille ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x8B]]></match>
+ <subst><![CDATA[<]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[[\x91-\x92]]]></match>
+ <subst><![CDATA[']]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[[\x93-\x94]]]></match>
+ <subst><![CDATA["]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[[\x96-\x97]]]></match>
+ <subst><![CDATA[-]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x98]]></match>
+ <subst><![CDATA[~]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x99]]></match>
+ <subst><![CDATA[ trademark ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\x9B]]></match>
+ <subst><![CDATA[>]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[[\x80-\x9F]]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\{]]></match>
+ <subst><![CDATA[ left curly brace ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\}]]></match>
+ <subst><![CDATA[ right curly brace ]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/kmail.xml b/kttsd/filters/stringreplacer/kmail.xml
new file mode 100644
index 0000000..b0f33d6
--- /dev/null
+++ b/kttsd/filters/stringreplacer/kmail.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Filter by David Power achiestdragon@gmail.com -->
+<wordlist>
+ <name>kmail notify</name>
+ <appid>knotify</appid>
+ <appid>kmail</appid>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<br>]]></match>
+ <subst><![CDATA[,]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</b>]]></match>
+ <subst><![CDATA[,]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[in /local/inbox]]></match>
+ <subst><![CDATA[,]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<b>]]></match>
+ <subst><![CDATA[,]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/kttsd_stringreplacerplugin.desktop b/kttsd/filters/stringreplacer/kttsd_stringreplacerplugin.desktop
new file mode 100644
index 0000000..df8214c
--- /dev/null
+++ b/kttsd/filters/stringreplacer/kttsd_stringreplacerplugin.desktop
@@ -0,0 +1,92 @@
+[Desktop Entry]
+Name=String Replacer
+Name[bg]=Замяна на низове
+Name[ca]=Reemplaçador de cadenes
+Name[cs]=Nahrazení řetězce
+Name[da]=Streng-erstatter
+Name[de]=Zeichenkettenersetzer
+Name[el]=Αντικαταστάτης συμβολοσειρών
+Name[es]=Reemplazador de cadenas
+Name[et]=Stringiasendaja
+Name[eu]=Kate-ordezkatzailea
+Name[fa]=جایگزین‌کنندۀ رشته
+Name[fi]=Tekstien korvaaja
+Name[fr]=Remplacement de chaînes
+Name[ga]=Ionadóir Teaghrán
+Name[gl]=Reemprazamento de Cadeas
+Name[he]=מחליף מחרוזות
+Name[hu]=Sztringcserélő
+Name[is]=Strengja útskiptir
+Name[it]=Sostitutore di stringhe
+Name[ja]=文字列置換
+Name[ka]=სტრიქონის ჩამნაცვლებელი
+Name[km]= ឧបករណ៍​ជំនួស​ខ្សែ​អក្សរ
+Name[lt]=Eilučių pakeitiklis
+Name[mk]=Менувач на низи знаци
+Name[ms]=Pengganti Rentetan
+Name[nb]=Strengerstatter
+Name[nds]=Tekenkeed-Tuscher
+Name[ne]=स्ट्रिङ प्रतिस्थापक
+Name[nl]=Tekstvervanger
+Name[pa]=ਸਤਰ ਤਬਦੀਲ
+Name[pl]=Zastępowanie tekstu
+Name[pt]=Substituição de Textos
+Name[pt_BR]=Substituidor de Strings
+Name[ru]=Автозамена
+Name[sk]=Náhrada reťazca
+Name[sl]=Zamenjevalnik nizov
+Name[sr]=Замењивач знаковних низова
+Name[sr@Latn]=Zamenjivač znakovnih nizova
+Name[sv]=Strängersättning
+Name[ta]=சர இடமாற்றி
+Name[tg]=Ҷойивазкунии автоматӣ
+Name[tr]=Metin Değiştirici
+Name[uk]=Заміна рядків
+Name[vi]=Trình thay Chuỗi
+Name[zh_TW]=字串置換器
+Comment=Generic String Replacement Filter Plugin for KTTS
+Comment[bg]=Приставка филтър за замяна на низове за KTTS
+Comment[ca]=Connector del filtre reemplaçador genèric de cadenes pel KTTS
+Comment[cs]=Modul filtru náhrady řetězce KTTS
+Comment[da]=Generisk strengerstatnings-filter-plugin for KTTS
+Comment[de]=Ein generisches Modul zur Zeichenkettenersetzung für KTTS
+Comment[el]=Γενικό φίλτρο πρόσθετο αντικατάστασης συμβολοσειρών για το KTTS
+Comment[es]=Complemento de filtro genérico de sustituición de cadenas para KTTS
+Comment[et]=KTTS-i üldine stringiasendusfiltri plugin
+Comment[eu]=KTTS-ren kateak ordezkatzeko iragazki-plugin generikoa
+Comment[fa]=وصلۀ پالایۀ جایگزینی رشتۀ عمومی برای KTTS
+Comment[fi]=Yleinen tekstien korvaussuodatinliitännäinen KTTS-ohjelmaan
+Comment[fr]=Module de filtrage de remplacement de chaînes générique pour KTTS
+Comment[gl]=Plugin de Filtro Xenérico de Substituición de Cadeas para KTTS
+Comment[hu]=Általános sztringcserélő modul a KTTS-hez
+Comment[is]=Almennt strengja útskiptinga íforrit fyrir KTTS
+Comment[it]=Plugin generico per il filtro di sostituzione di stringhe per KTTS
+Comment[ja]=KTTS 用汎用文字列置換フィルタプラグイン
+Comment[ka]=ზოგადი სტრიქონის ჩანაცვლების ფილტრის მოდული KTTS-სთვის
+Comment[km]=កម្មវិធី​ជំនួយ​តម្រង​ការ​ជំនួស​ខ្សែអក្សរ​ទូទៅ​សម្រាប់ KTTS
+Comment[lt]=Bendro pobūdžio eilučių pakeitimo filtro priedas, skirtas KTTS
+Comment[mk]=Филтер за KTTS за општа замена на низи знаци
+Comment[ms]=Plugin Penapis Penggantian Rentetan Generik bagi KTTS
+Comment[nb]=Filter-programtillegg til KTTS for streng-erstatning
+Comment[nds]=KTTS-Filtermoduul för dat Utwesseln vun Tekenkeden
+Comment[ne]=KTTS का लागि जेनेरीक स्ट्रिङ प्रतिस्थापन फिल्टर प्लगइन
+Comment[nl]=Generieke tekst-vervang-filter-plugin voor KTTS
+Comment[pl]=Wtyczka zastępowania tekstu dla KTTS
+Comment[pt]='Plugin' Genérico de Substituição de Textos para o KTTS
+Comment[pt_BR]=Plug-in de Filtro genérico de Substituição de Strings Para o KTTS
+Comment[ru]=Фильтр автозамены для KTTS
+Comment[sk]=Všeobecný modul filtra náhrady reťazca v KTTS
+Comment[sl]=Filtrni vstavek KTTS za generični zamenjevalnik nizov
+Comment[sr]=Генерички филтерски прикључак KTTS-а за замену знаковних низова
+Comment[sr@Latn]=Generički filterski priključak KTTS-a za zamenu znakovnih nizova
+Comment[sv]=Insticksprogram för KTTS med generellt strängersättningsfilter
+Comment[ta]=KTTSக்கான அலங்கார சொருகுபொருள் சர இடமாற்றி
+Comment[tg]=Филтри худивазкунӣ барои KTTS
+Comment[tr]=KTTS için Genel Metin Değiştirme Süzgeci Eklentisi
+Comment[uk]=Втулок загального фільтра заміни рядків для KTTS
+Comment[vi]=Trình bổ sung Lọc Thay Chuỗi Chung cho KTTS
+Comment[zh_TW]=KTTS 使用的字串置換外掛程式
+Type=Service
+ServiceTypes=KTTSD/FilterPlugin
+X-KDE-Library=libkttsd_stringreplacerplugin
+X-KDE-Languages=en,en_US,en_GB,en_CA,es,es_mx,cy,de,fi,cs,pl
diff --git a/kttsd/filters/stringreplacer/polish_festival_fixes.xml b/kttsd/filters/stringreplacer/polish_festival_fixes.xml
new file mode 100644
index 0000000..a6f2533
--- /dev/null
+++ b/kttsd/filters/stringreplacer/polish_festival_fixes.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author of filter: Mikolaj Machowski ( mikmach AT wp DOT pl ) -->
+<wordlist>
+ <name>Filtr polski / Filter for Polish Festival voice</name>
+ <language-code>pl</language-code>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[[^a-zA-Z0-9ęóąśłżźćńĘÓĄŚŁŻŹĆŃ\,\"\'\\@#$%^*+=`~]]]></match>
+ <subst><![CDATA[ ]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/polish_festival_unspeakables.xml b/kttsd/filters/stringreplacer/polish_festival_unspeakables.xml
new file mode 100644
index 0000000..8fbc18a
--- /dev/null
+++ b/kttsd/filters/stringreplacer/polish_festival_unspeakables.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author of filter: Mikolaj Machowski ( mikmach AT wp DOT pl ) -->
+<wordlist>
+ <name>Polskie niewymawialne / Polish Festival unspeakables</name>
+ <language-code>pl</language-code>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\(]]></match>
+ <subst><![CDATA[ otwieram nawias ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\)]]></match>
+ <subst><![CDATA[ zamykam nawias ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\{]]></match>
+ <subst><![CDATA[ otwieram nawias klamrowy ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\}]]></match>
+ <subst><![CDATA[ zamykam nawias klamrowy ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\[]]></match>
+ <subst><![CDATA[ otwieram nawias kwadratowy ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\]]]></match>
+ <subst><![CDATA[ zamykam nawias kwadratowy ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\<]]></match>
+ <subst><![CDATA[ otwieram nawias trójkątny ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\>]]></match>
+ <subst><![CDATA[ zamykam nawias trójkątny ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\_]]></match>
+ <subst><![CDATA[ znak podkreślenia ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\&]]></match>
+ <subst><![CDATA[ i ]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[\/]]></match>
+ <subst><![CDATA[ slesz ]]></subst>
+ </word>
+ <word>
+ <type>Word</type>
+ <case>No</case>
+ <match><![CDATA[\\]]></match>
+ <subst><![CDATA[ bekslesz ]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/qt2plaintext.xml b/kttsd/filters/stringreplacer/qt2plaintext.xml
new file mode 100644
index 0000000..8a194ec
--- /dev/null
+++ b/kttsd/filters/stringreplacer/qt2plaintext.xml
@@ -0,0 +1,359 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wordlist>
+ <name>Qt Rich Text to Plain Text</name>
+ <appid>knotify</appid>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<qt>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</qt>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<h1>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<h2>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<h3>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</h1>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</h2>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</h3>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<p>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</p>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<center>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</center>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<blockquote>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</blockquote>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<ul>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</ul>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<ol>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</ol>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<li>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</li>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<a>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</a>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<em>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</em>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<strong>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</strong>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<bold>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</bold>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<b>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</b>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<u>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</u>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<big>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</big>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<small>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</small>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<hr/>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<br/>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<nobr>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</nobr>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<table>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</table>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<tr>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</tr>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<td>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</td>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<th>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</th>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<img src\=]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<a href\=]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[/>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<img>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[">]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<pre>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</pre>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<tt>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</tt>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[<code>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+ <word>
+ <type>RegExp</type>
+ <case>No</case>
+ <match><![CDATA[</code>]]></match>
+ <subst><![CDATA[]]></subst>
+ </word>
+</wordlist>
diff --git a/kttsd/filters/stringreplacer/stringreplacerconf.cpp b/kttsd/filters/stringreplacer/stringreplacerconf.cpp
new file mode 100644
index 0000000..9afa2d5
--- /dev/null
+++ b/kttsd/filters/stringreplacer/stringreplacerconf.cpp
@@ -0,0 +1,725 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic String Replacement Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qradiobutton.h>
+
+// KDE includes.
+#include <kglobal.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+#include <kpushbutton.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kregexpeditorinterface.h>
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// StringReplacer includes.
+#include "stringreplacerconf.h"
+#include "stringreplacerconf.moc"
+#include "editreplacementwidget.h"
+
+/**
+* Constructor
+*/
+StringReplacerConf::StringReplacerConf( QWidget *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterConf(parent, name),
+ m_editDlg(0),
+ m_editWidget(0)
+{
+ // kdDebug() << "StringReplacerConf::StringReplacerConf: Running" << endl;
+
+ // Create configuration widget.
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint(), "StringReplacerConfigWidgetLayout");
+ layout->setAlignment (Qt::AlignTop);
+ m_widget = new StringReplacerConfWidget(this, "StringReplacerConfigWidget");
+ layout->addWidget(m_widget);
+ m_widget->substLView->setSortColumn(-1);
+
+ connect(m_widget->nameLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->languageBrowseButton, SIGNAL(clicked()),
+ this, SLOT(slotLanguageBrowseButton_clicked()));
+ connect(m_widget->addButton, SIGNAL(clicked()),
+ this, SLOT(slotAddButton_clicked()));
+ connect(m_widget->upButton, SIGNAL(clicked()),
+ this, SLOT(slotUpButton_clicked()));
+ connect(m_widget->downButton, SIGNAL(clicked()),
+ this, SLOT(slotDownButton_clicked()));
+ connect(m_widget->editButton, SIGNAL(clicked()),
+ this, SLOT(slotEditButton_clicked()));
+ connect(m_widget->removeButton, SIGNAL(clicked()),
+ this, SLOT(slotRemoveButton_clicked()));
+ connect(m_widget->loadButton, SIGNAL(clicked()),
+ this, SLOT(slotLoadButton_clicked()));
+ connect(m_widget->saveButton, SIGNAL(clicked()),
+ this, SLOT(slotSaveButton_clicked()));
+ connect(m_widget->clearButton, SIGNAL(clicked()),
+ this, SLOT(slotClearButton_clicked()));
+ connect(m_widget->substLView, SIGNAL(selectionChanged()),
+ this, SLOT(enableDisableButtons()));
+ connect(m_widget->appIdLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+
+ // Determine if kdeutils Regular Expression Editor is installed.
+ m_reEditorInstalled = !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty();
+
+ // Set up defaults.
+ defaults();
+}
+
+/**
+* Destructor.
+*/
+StringReplacerConf::~StringReplacerConf(){
+ // kdDebug() << "StringReplacerConf::~StringReplacerConf: Running" << endl;
+}
+
+/**
+* This method is invoked whenever the module should read its
+* configuration (most of the times from a config file) and update the
+* user interface. This happens when the user clicks the "Reset" button in
+* the control center, to undo all of his changes and restore the currently
+* valid settings. Note that kttsmgr calls this when the plugin is
+* loaded, so it not necessary to call it in your constructor.
+* The plugin should read its configuration from the specified group
+* in the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* loading your configuration.
+*/
+void StringReplacerConf::load(KConfig* config, const QString& configGroup){
+ // kdDebug() << "StringReplacerConf::load: Running" << endl;
+ // See if this filter previously save its word list.
+ config->setGroup( configGroup );
+ QString wordsFilename = config->readEntry( "WordListFile" );
+ if ( !wordsFilename.isEmpty() )
+ {
+ QString errMsg = loadFromFile( wordsFilename, true );
+ if ( !errMsg.isEmpty() )
+ kdDebug() << "StringReplacerConf::load: " << errMsg << endl;
+ enableDisableButtons();
+ }
+}
+
+// Loads word list and settings from a file. Clearing configuration if clear is True.
+QString StringReplacerConf::loadFromFile( const QString& filename, bool clear)
+{
+ // Open existing word list.
+ QFile file( filename );
+ if ( !file.open( IO_ReadOnly ) )
+ {
+ return i18n("Unable to open file.") + filename;
+ }
+ // QDomDocument doc( "http://www.kde.org/share/apps/kttsd/stringreplacer/wordlist.dtd []" );
+ QDomDocument doc( "" );
+ if ( !doc.setContent( &file ) ) {
+ file.close();
+ return i18n("File not in proper XML format.");
+ }
+ // kdDebug() << "StringReplacerConf::load: document successfully parsed." << endl;
+ file.close();
+
+ // Clear list view.
+ if ( clear ) m_widget->substLView->clear();
+
+ // Name setting.
+ QDomNodeList nameList = doc.elementsByTagName( "name" );
+ QDomNode nameNode = nameList.item( 0 );
+ m_widget->nameLineEdit->setText( nameNode.toElement().text() );
+ // kdDebug() << "StringReplacerConf::load: name = " << nameNode.toElement().text() << endl;
+
+ // Language Codes setting. List may be single element of comma-separated values,
+ // or multiple elements.
+ QString languageCodes;
+ QDomNodeList languageList = doc.elementsByTagName( "language-code" );
+ for ( uint ndx=0; ndx < languageList.count(); ++ndx )
+ {
+ QDomNode languageNode = languageList.item( ndx );
+ if (!languageCodes.isEmpty()) languageCodes += ",";
+ languageCodes += languageNode.toElement().text();
+ }
+ if ( clear )
+ m_languageCodeList = QStringList::split(',', languageCodes, false);
+ else
+ m_languageCodeList += QStringList::split(',', languageCodes, false);
+ QString language;
+ m_languageCodeList.sort();
+ // Eliminate dups.
+ for ( int ndx = m_languageCodeList.count() - 2; ndx >= 0; --ndx )
+ {
+ if ( m_languageCodeList[ndx] == m_languageCodeList[ndx+1] )
+ m_languageCodeList.remove(m_languageCodeList.at(ndx+1));
+ }
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx )
+ {
+ if (!language.isEmpty()) language += ",";
+ language += KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[ndx]);
+ }
+ m_widget->languageLineEdit->setText(language);
+
+ // AppId. Apply this filter only if DCOP appId of application that queued
+ // the text contains this string. List may be single element of comma-separated values,
+ // or multiple elements.
+ QDomNodeList appIdList = doc.elementsByTagName( "appid" );
+ QString appIds;
+ for ( uint ndx=0; ndx < appIdList.count(); ++ndx )
+ {
+ QDomNode appIdNode = appIdList.item( ndx );
+ if (!appIds.isEmpty()) appIds += ",";
+ appIds += appIdNode.toElement().text();
+ }
+ if ( !clear ) appIds = m_widget->appIdLineEdit->text() + appIds;
+ m_widget->appIdLineEdit->setText( appIds );
+
+ // Word list.
+ QListViewItem* item = 0;
+ if ( !clear ) item = m_widget->substLView->lastChild();
+ QDomNodeList wordList = doc.elementsByTagName("word");
+ const int wordListCount = wordList.count();
+ for (int wordIndex = 0; wordIndex < wordListCount; ++wordIndex)
+ {
+ // kdDebug() << "StringReplacerConf::load: start parsing of word " << wordIndex << endl;
+ QDomNode wordNode = wordList.item(wordIndex);
+ QDomNodeList propList = wordNode.childNodes();
+ QString wordType;
+ QString matchCase = "No"; // Default for old (v<=3.5.3) config files with no <case/>.
+ QString match;
+ QString subst;
+ const int propListCount = propList.count();
+ for (int propIndex = 0; propIndex < propListCount; ++propIndex)
+ {
+ QDomNode propNode = propList.item(propIndex);
+ QDomElement prop = propNode.toElement();
+ if (prop.tagName() == "type") wordType = prop.text();
+ if (prop.tagName() == "case") matchCase = prop.text();
+ if (prop.tagName() == "match") match = prop.text();
+ if (prop.tagName() == "subst") subst = prop.text();
+ }
+ QString wordTypeStr =
+ (wordType=="RegExp"?i18n("Abbreviation for 'Regular Expression'", "RegExp"):i18n("Word"));
+ QString matchCaseStr =
+ (matchCase=="Yes"?i18n("Yes"):i18n("No"));
+ if (!item)
+ item = new KListViewItem(m_widget->substLView, wordTypeStr, matchCaseStr, match, subst);
+ else
+ item = new KListViewItem(m_widget->substLView, item, wordTypeStr, matchCaseStr, match, subst);
+ }
+
+ return QString::null;
+}
+
+/**
+* This function gets called when the user wants to save the settings in
+* the user interface, updating the config files or wherever the
+* configuration is stored. The method is called when the user clicks "Apply"
+* or "Ok". The plugin should save its configuration in the specified
+* group of the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* saving your configuration.
+*/
+void StringReplacerConf::save(KConfig* config, const QString& configGroup){
+ // kdDebug() << "StringReplacerConf::save: Running" << endl;
+ QString wordsFilename =
+ KGlobal::dirs()->saveLocation( "data" ,"kttsd/stringreplacer/", true );
+ if ( wordsFilename.isEmpty() )
+ {
+ kdDebug() << "StringReplacerConf::save: no save location" << endl;
+ return;
+ }
+ wordsFilename += configGroup;
+ QString errMsg = saveToFile( wordsFilename );
+ if ( errMsg.isEmpty() )
+ {
+ config->setGroup( configGroup );
+ config->writeEntry( "WordListFile", realFilePath(wordsFilename) );
+ }
+ else
+ kdDebug() << "StringReplacerConf::save: " << errMsg << endl;
+}
+
+// Saves word list and settings to a file.
+QString StringReplacerConf::saveToFile(const QString& filename)
+{
+ // kdDebug() << "StringReplacerConf::saveToFile: saving to file " << wordsFilename << endl;
+
+ QFile file( filename );
+ if ( !file.open( IO_WriteOnly ) )
+ return i18n("Unable to open file ") + filename;
+
+ // QDomDocument doc( "http://www.kde.org/share/apps/kttsd/stringreplacer/wordlist.dtd []" );
+ QDomDocument doc( "" );
+
+ QDomElement root = doc.createElement( "wordlist" );
+ doc.appendChild( root );
+
+ // Name.
+ QDomElement name = doc.createElement( "name" );
+ root.appendChild( name );
+ QDomText t = doc.createTextNode( m_widget->nameLineEdit->text() );
+ name.appendChild( t );
+
+ // Language code.
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx )
+ {
+ QDomElement languageCode = doc.createElement( "language-code" );
+ root.appendChild( languageCode );
+ t = doc.createTextNode( m_languageCodeList[ndx] );
+ languageCode.appendChild( t );
+ }
+
+ // Application ID
+ QString appId = m_widget->appIdLineEdit->text().replace(" ", "");
+ if ( !appId.isEmpty() )
+ {
+ QStringList appIdList = QStringList::split(",", appId);
+ for ( uint ndx=0; ndx < appIdList.count(); ++ndx )
+ {
+ QDomElement appIdElem = doc.createElement( "appid" );
+ root.appendChild( appIdElem );
+ t = doc.createTextNode( appIdList[ndx] );
+ appIdElem.appendChild( t );
+ }
+ }
+
+ // Words.
+ QListView* lView = m_widget->substLView;
+ QListViewItem* item = lView->firstChild();
+ while (item)
+ {
+ QDomElement wordTag = doc.createElement( "word" );
+ root.appendChild( wordTag );
+ QDomElement propTag = doc.createElement( "type" );
+ wordTag.appendChild( propTag);
+ QDomText t = doc.createTextNode( item->text(0)==i18n("Word")?"Word":"RegExp" );
+ propTag.appendChild( t );
+
+ propTag = doc.createElement( "case" );
+ wordTag.appendChild( propTag);
+ t = doc.createTextNode( item->text(1)==i18n("Yes")?"Yes":"No" );
+ propTag.appendChild( t );
+
+ propTag = doc.createElement( "match" );
+ wordTag.appendChild( propTag);
+ t = doc.createCDATASection( item->text(2) );
+ propTag.appendChild( t );
+
+ propTag = doc.createElement( "subst" );
+ wordTag.appendChild( propTag);
+ t = doc.createCDATASection( item->text(3) );
+ propTag.appendChild( t );
+
+ item = item->nextSibling();
+ }
+
+ // Write it all out.
+ QTextStream ts( &file );
+ ts.setEncoding( QTextStream::UnicodeUTF8 );
+ ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ ts << doc.toString();
+ // kdDebug() << "StringReplacerConf::saveToFile: writing out " << doc.toString() << endl;
+ file.close();
+
+ return QString::null;
+}
+
+/**
+* This function is called to set the settings in the module to sensible
+* default values. It gets called when hitting the "Default" button. The
+* default values should probably be the same as the ones the application
+* uses when started without a config file. Note that defaults should
+* be applied to the on-screen widgets; not to the config file.
+*/
+void StringReplacerConf::defaults(){
+ // kdDebug() << "StringReplacerConf::defaults: Running" << endl;
+ // Default language is none.
+ m_languageCodeList.clear();
+ m_widget->languageLineEdit->setText( "" );
+ // Default name.
+ m_widget->nameLineEdit->setText( i18n("String Replacer") );
+ m_widget->substLView->clear();
+ // Default App ID is blank.
+ m_widget->appIdLineEdit->setText( "" );
+ enableDisableButtons();
+ // kdDebug() << "StringReplacerConf::defaults: Exiting" << endl;
+}
+
+/**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+bool StringReplacerConf::supportsMultiInstance() { return true; }
+
+/**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+QString StringReplacerConf::userPlugInName()
+{
+ if ( m_widget->substLView->childCount() == 0 ) return QString::null;
+ QString instName = m_widget->nameLineEdit->text();
+ if ( instName.isEmpty() )
+ {
+ QString language;
+ if (m_languageCodeList.count() == 1)
+ language = KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[0]);
+ if (m_languageCodeList.count() > 1)
+ language = i18n("Multiple Languages");
+ if (!language.isEmpty())
+ instName = i18n("String Replacer") + " (" + language + ")";
+ }
+ return instName;
+}
+
+void StringReplacerConf::slotLanguageBrowseButton_clicked()
+{
+ // Create a QHBox to host KListView.
+ QHBox* hBox = new QHBox(m_widget, "SelectLanguage_hbox");
+ // Create a KListView and fill with all known languages.
+ KListView* langLView = new KListView(hBox, "SelectLanguage_lview");
+ langLView->addColumn(i18n("Language"));
+ langLView->addColumn(i18n("Code"));
+ langLView->setSelectionMode(QListView::Extended);
+ QStringList allLocales = KGlobal::locale()->allLanguagesTwoAlpha();
+ QString locale;
+ QString languageCode;
+ QString countryCode;
+ QString charSet;
+ QString language;
+ // Blank line so user can select no language.
+ QListViewItem* item = new KListViewItem(langLView, "", "");
+ if (m_languageCodeList.isEmpty()) item->setSelected(true);
+ const int allLocalesCount = allLocales.count();
+ for (int ndx=0; ndx < allLocalesCount; ++ndx)
+ {
+ locale = allLocales[ndx];
+ KGlobal::locale()->splitLocale(locale, languageCode, countryCode, charSet);
+ language = KGlobal::locale()->twoAlphaToLanguageName(languageCode);
+ if (!countryCode.isEmpty()) language +=
+ " (" + KGlobal::locale()->twoAlphaToCountryName(countryCode)+")";
+ item = new KListViewItem(langLView, language, locale);
+ if (m_languageCodeList.contains(locale)) item->setSelected(true);
+ }
+ // Sort by language.
+ langLView->setSorting(0);
+ langLView->sort();
+ // Display the box in a dialog.
+ KDialogBase* dlg = new KDialogBase(
+ KDialogBase::Swallow,
+ i18n("Select Languages"),
+ KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ m_widget,
+ "SelectLanguage_dlg",
+ true,
+ true);
+ dlg->setMainWidget(hBox);
+ dlg->setHelp("", "kttsd");
+ dlg->setInitialSize(QSize(300, 500), false);
+ int dlgResult = dlg->exec();
+ languageCode = QString::null;
+ if (dlgResult == QDialog::Accepted)
+ {
+ m_languageCodeList.clear();
+ QListViewItem* item = langLView->firstChild();
+ while (item)
+ {
+ if (item->isSelected()) m_languageCodeList += item->text(1);
+ item = item->nextSibling();
+ }
+ }
+ delete dlg;
+ // TODO: Also delete KListView and QHBox?
+ if (dlgResult != QDialog::Accepted) return;
+ language = "";
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx)
+ {
+ if (!language.isEmpty()) language += ",";
+ language += KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[ndx]);
+ }
+ QString s1 = m_widget->languageLineEdit->text();
+ m_widget->languageLineEdit->setText(language);
+ // Replace language in the user's filter name.
+ QString s2 = m_widget->nameLineEdit->text();
+ if (m_languageCodeList.count() > 1) language = i18n("Multiple Languages");
+ if ( !s1.isEmpty() )
+ {
+ s2.replace( s1, language );
+ s2.replace( i18n("Multiple Languages"), language );
+ }
+ s2.replace(" ()", "");
+ if ( !s2.contains("(") && !language.isEmpty() ) s2 += " (" + language + ")";
+ m_widget->nameLineEdit->setText(s2);
+ configChanged();
+}
+
+void StringReplacerConf::enableDisableButtons()
+{
+ bool enableBtn = (m_widget->substLView->selectedItem() != 0);
+ if (enableBtn)
+ {
+ m_widget->upButton->setEnabled(
+ m_widget->substLView->selectedItem()->itemAbove() != 0);
+ m_widget->downButton->setEnabled(
+ m_widget->substLView->selectedItem()->itemBelow() != 0);
+ } else {
+ m_widget->upButton->setEnabled(false);
+ m_widget->downButton->setEnabled(false);
+ }
+ m_widget->editButton->setEnabled(enableBtn);
+ m_widget->removeButton->setEnabled(enableBtn);
+ m_widget->clearButton->setEnabled(m_widget->substLView->firstChild());
+ m_widget->saveButton->setEnabled(m_widget->substLView->firstChild());
+}
+
+void StringReplacerConf::slotUpButton_clicked()
+{
+ QListViewItem* item = m_widget->substLView->selectedItem();
+ if (!item) return;
+ QListViewItem* prevItem = item->itemAbove();
+ if (!prevItem) return;
+ prevItem->moveItem(item);
+ m_widget->substLView->setSelected(item, true);
+ m_widget->substLView->ensureItemVisible(item);
+ enableDisableButtons();
+ configChanged();
+}
+
+void StringReplacerConf::slotDownButton_clicked()
+{
+ QListViewItem* item = m_widget->substLView->selectedItem();
+ if (!item) return;
+ QListViewItem* nextItem = item->itemBelow();
+ if (!nextItem) return;
+ item->moveItem(nextItem);
+ m_widget->substLView->setSelected(item, true);
+ m_widget->substLView->ensureItemVisible(item);
+ enableDisableButtons();
+ configChanged();
+}
+
+void StringReplacerConf::slotAddButton_clicked()
+{
+ addOrEditSubstitution( true );
+}
+
+void StringReplacerConf::slotEditButton_clicked()
+{
+ addOrEditSubstitution( false );
+}
+
+// Displays the add/edit string replacement dialog.
+void StringReplacerConf::addOrEditSubstitution(bool isAdd)
+{
+ QListViewItem* item = 0;
+ if (isAdd)
+ item = m_widget->substLView->lastChild();
+ else
+ {
+ item = m_widget->substLView->selectedItem();
+ if (!item) return;
+ }
+ // Create a QHBox to host widget.
+ QHBox* hBox = new QHBox(m_widget, "AddOrEditSubstitution_hbox" );
+ // Create widget.
+ m_editWidget = new EditReplacementWidget( hBox, "AddOrEditSubstitution_widget" );
+ // Set controls if editing existing.
+ m_editWidget->matchButton->setEnabled( false );
+ if (!isAdd)
+ {
+ if ( item->text(0) == i18n("Abbreviation for 'Regular Expression'", "RegExp") )
+ {
+ m_editWidget->regexpRadioButton->setChecked( true );
+ m_editWidget->matchButton->setEnabled( m_reEditorInstalled );
+ }
+ m_editWidget->caseCheckBox->setChecked( (item->text(1))==i18n("Yes") );
+ m_editWidget->matchLineEdit->setText( item->text(2) );
+ m_editWidget->substLineEdit->setText( item->text(3) );
+ }
+ // The match box may not be blank.
+ connect( m_editWidget->matchLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotMatchLineEdit_textChanged(const QString&)) );
+ connect( m_editWidget->regexpRadioButton, SIGNAL(clicked()),
+ this, SLOT(slotTypeButtonGroup_clicked()) );
+ connect( m_editWidget->wordRadioButton, SIGNAL(clicked()),
+ this, SLOT(slotTypeButtonGroup_clicked()) );
+ connect( m_editWidget->matchButton, SIGNAL(clicked()),
+ this, SLOT(slotMatchButton_clicked()) );
+ // Display the box in a dialog.
+ m_editDlg = new KDialogBase(
+ KDialogBase::Swallow,
+ i18n("Edit String Replacement"),
+ KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ m_widget,
+ "AddOrEditSubstitution_dlg",
+ true,
+ true);
+ // Disable OK button if match field blank.
+ m_editDlg->setMainWidget( hBox );
+ m_editDlg->setHelp( "", "kttsd" );
+ m_editDlg->enableButton( KDialogBase::Ok, !m_editWidget->matchLineEdit->text().isEmpty() );
+ int dlgResult = m_editDlg->exec();
+ QString substType = i18n( "Word" );
+ if ( m_editWidget->regexpRadioButton->isChecked() )
+ substType = i18n("Abbreviation for 'Regular Expression'", "RegExp");
+ QString matchCase = i18n("No");
+ if ( m_editWidget->caseCheckBox->isChecked() ) matchCase = i18n("Yes");
+ QString match = m_editWidget->matchLineEdit->text();
+ QString subst = m_editWidget->substLineEdit->text();
+ delete m_editDlg;
+ m_editDlg = 0;
+ m_editWidget = 0;
+ if (dlgResult != QDialog::Accepted) return;
+ // TODO: Also delete hBox and w?
+ if ( match.isEmpty() ) return;
+ if ( isAdd )
+ {
+ if ( item )
+ item = new KListViewItem( m_widget->substLView, item, substType, matchCase, match, subst );
+ else
+ item = new KListViewItem( m_widget->substLView, substType, matchCase, match, subst );
+ m_widget->substLView->setSelected( item, true );
+ }
+ else
+ {
+ item->setText( 0, substType );
+ item->setText( 1, matchCase );
+ item->setText( 2, match );
+ item->setText( 3, subst );
+ }
+ m_widget->substLView->ensureItemVisible( item );
+ enableDisableButtons();
+ configChanged();
+}
+
+void StringReplacerConf::slotMatchLineEdit_textChanged(const QString& text)
+{
+ // Disable OK button if match field blank.
+ if ( !m_editDlg ) return;
+ m_editDlg->enableButton( KDialogBase::Ok, !text.isEmpty() );
+}
+
+void StringReplacerConf::slotRemoveButton_clicked()
+{
+ QListViewItem* item = m_widget->substLView->selectedItem();
+ if (!item) return;
+ delete item;
+ enableDisableButtons();
+ configChanged();
+}
+
+void StringReplacerConf::slotTypeButtonGroup_clicked()
+{
+ // Enable Regular Expression Editor button if editor is installed (requires kdeutils).
+ if ( !m_editWidget ) return;
+ m_editWidget->matchButton->setEnabled( m_editWidget->regexpRadioButton->isOn() && m_reEditorInstalled );
+}
+
+void StringReplacerConf::slotMatchButton_clicked()
+{
+ // Show Regular Expression Editor dialog if it is installed.
+ if ( !m_editWidget ) return;
+ if ( !m_editDlg ) return;
+ if ( !m_reEditorInstalled ) return;
+ QDialog *editorDialog =
+ KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+ if ( editorDialog )
+ {
+ // kdeutils was installed, so the dialog was found. Fetch the editor interface.
+ KRegExpEditorInterface *reEditor =
+ static_cast<KRegExpEditorInterface *>(editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT( reEditor ); // This should not fail!// now use the editor.
+ reEditor->setRegExp( m_editWidget->matchLineEdit->text() );
+ int dlgResult = editorDialog->exec();
+ if ( dlgResult == QDialog::Accepted )
+ {
+ QString re = reEditor->regExp();
+ m_editWidget->matchLineEdit->setText( re );
+ m_editDlg->enableButton( KDialogBase::Ok, !re.isEmpty() );
+ }
+ delete editorDialog;
+ } else return;
+}
+
+void StringReplacerConf::slotLoadButton_clicked()
+{
+ // QString dataDir = KGlobal::dirs()->resourceDirs("data").last() + "/kttsd/stringreplacer/";
+ QString dataDir = KGlobal::dirs()->findAllResources("data", "kttsd/stringreplacer/").last();
+ QString filename = KFileDialog::getOpenFileName(
+ dataDir,
+ "*.xml|String Replacer Word List (*.xml)",
+ m_widget,
+ "stringreplacer_loadfile");
+ if ( filename.isEmpty() ) return;
+ QString errMsg = loadFromFile( filename, false );
+ enableDisableButtons();
+ if ( !errMsg.isEmpty() )
+ KMessageBox::sorry( m_widget, errMsg, i18n("Error Opening File") );
+ else
+ configChanged();
+}
+
+void StringReplacerConf::slotSaveButton_clicked()
+{
+ QString filename = KFileDialog::getSaveFileName(
+ KGlobal::dirs()->saveLocation( "data" ,"kttsd/stringreplacer/", false ),
+ "*.xml|String Replacer Word List (*.xml)",
+ m_widget,
+ "stringreplacer_savefile");
+ if ( filename.isEmpty() ) return;
+ QString errMsg = saveToFile( filename );
+ enableDisableButtons();
+ if ( !errMsg.isEmpty() )
+ KMessageBox::sorry( m_widget, errMsg, i18n("Error Opening File") );
+}
+
+void StringReplacerConf::slotClearButton_clicked()
+{
+ m_widget->substLView->clear();
+ enableDisableButtons();
+}
diff --git a/kttsd/filters/stringreplacer/stringreplacerconf.h b/kttsd/filters/stringreplacer/stringreplacerconf.h
new file mode 100644
index 0000000..f0312a6
--- /dev/null
+++ b/kttsd/filters/stringreplacer/stringreplacerconf.h
@@ -0,0 +1,158 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic String Replacement Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _STRINGREPLACERCONF_H_
+#define _STRINGREPLACERCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// StringReplacer includes.
+#include "stringreplacerconfwidget.h"
+
+class KDialogBase;
+class EditReplacementWidget;
+
+class StringReplacerConf : public KttsFilterConf
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ StringReplacerConf( QWidget *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor
+ */
+ virtual ~StringReplacerConf();
+
+ enum SubstitutionType {
+ stWord, // Word
+ stRegExp // Regular Expression
+ };
+
+ /**
+ * This method is invoked whenever the module should read its
+ * configuration (most of the times from a config file) and update the
+ * user interface. This happens when the user clicks the "Reset" button in
+ * the control center, to undo all of his changes and restore the currently
+ * valid settings. Note that KTTSMGR calls this when the plugin is
+ * loaded, so it not necessary to call it in your constructor.
+ * The plugin should read its configuration from the specified group
+ * in the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * loading your configuration.
+ *
+ * When a plugin is first added to KTTSMGR, @e load will be called with
+ * a Null @e configGroup. In this case, the plugin will not have
+ * any instance-specific parameters to load, but it may still wish
+ * to load parameters that apply to all instances of the plugin.
+ */
+ virtual void load(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function gets called when the user wants to save the settings in
+ * the user interface, updating the config files or wherever the
+ * configuration is stored. The method is called when the user clicks "Apply"
+ * or "Ok". The plugin should save its configuration in the specified
+ * group of the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * saving your configuration.
+ */
+ virtual void save(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function is called to set the settings in the module to sensible
+ * default values. It gets called when hitting the "Default" button. The
+ * default values should probably be the same as the ones the application
+ * uses when started without a config file. Note that defaults should
+ * be applied to the on-screen widgets; not to the config file.
+ */
+ virtual void defaults();
+
+ /**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+ virtual bool supportsMultiInstance();
+
+ /**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+ virtual QString userPlugInName();
+
+ private slots:
+ void slotLanguageBrowseButton_clicked();
+ void slotAddButton_clicked();
+ void slotUpButton_clicked();
+ void slotDownButton_clicked();
+ void slotEditButton_clicked();
+ void slotRemoveButton_clicked();
+ void slotMatchLineEdit_textChanged(const QString& text);
+ void slotTypeButtonGroup_clicked();
+ void slotMatchButton_clicked();
+ void slotLoadButton_clicked();
+ void slotSaveButton_clicked();
+ void slotClearButton_clicked();
+
+ // EnablesDisables buttons depending upon current item in list view.
+ void enableDisableButtons();
+
+ private:
+ // Displays the add/edit string replacement dialog.
+ void addOrEditSubstitution(bool isAdd);
+ // Loads word list and settings from a file. Clearing configuration if clear is True.
+ QString loadFromFile( const QString& filename, bool clear);
+ // Saves word list and settings to a file.
+ QString saveToFile( const QString& filename );
+
+
+ // Configuration Widget.
+ StringReplacerConfWidget* m_widget;
+ // Edit Dialog and widget.
+ KDialogBase* m_editDlg;
+ EditReplacementWidget* m_editWidget;
+ // True if kdeutils Regular Expression Editor is installed.
+ bool m_reEditorInstalled;
+ // Language Codes.
+ QStringList m_languageCodeList;
+};
+
+#endif //_STRINGREPLACERCONF_H_
diff --git a/kttsd/filters/stringreplacer/stringreplacerconfwidget.ui b/kttsd/filters/stringreplacer/stringreplacerconfwidget.ui
new file mode 100644
index 0000000..e88307f
--- /dev/null
+++ b/kttsd/filters/stringreplacer/stringreplacerconfwidget.ui
@@ -0,0 +1,474 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>StringReplacerConfWidget</class>
+<comment>Widget for configuring String Replacer Filter for KTTSD.</comment>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>StringReplacerConfWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>548</width>
+ <height>421</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Configure String Replacer</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter any name you like for this filter.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter any name you like for this filter.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="3" column="0">
+ <property name="name">
+ <cstring>applyGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Apply This &amp;Filter When</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These settings determines when the filter is applied to text.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>languageLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Lan&amp;guage is:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank the filter applies to all text jobs of any language.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>appIdLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Application &amp;ID contains:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank the filter applies to all text jobs of any language.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>languageBrowseButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to select one or more languages. This filter will be applied to text jobs of those languages.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. Use &lt;b&gt;knotify&lt;/b&gt; to match all messages sent as KDE notifications. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>loadButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Load...</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to load a word list from a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>saveButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Save...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to save word list to a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>clearButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>C&amp;lear</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to empty the word list.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout24</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Match Case</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Match</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Replace With</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>substLView</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>addButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Add...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to add another word or regular expression to the list.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>upButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Up</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to move selected word up in the list. Words higher in the list are applied first.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>downButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Do&amp;wn</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to move a word down in the list. Words lower in the list are applied last.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>editButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Edit...</string>
+ </property>
+ <property name="toggleButton">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to modify an existing word or regular expression in the list.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ <property name="toggleButton">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to remove a word or regular expression from the list.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>nameLineEdit</tabstop>
+ <tabstop>substLView</tabstop>
+ <tabstop>addButton</tabstop>
+ <tabstop>upButton</tabstop>
+ <tabstop>downButton</tabstop>
+ <tabstop>editButton</tabstop>
+ <tabstop>removeButton</tabstop>
+ <tabstop>loadButton</tabstop>
+ <tabstop>saveButton</tabstop>
+ <tabstop>clearButton</tabstop>
+ <tabstop>languageLineEdit</tabstop>
+ <tabstop>languageBrowseButton</tabstop>
+ <tabstop>appIdLineEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/filters/stringreplacer/stringreplacerplugin.cpp b/kttsd/filters/stringreplacer/stringreplacerplugin.cpp
new file mode 100644
index 0000000..c3f9678
--- /dev/null
+++ b/kttsd/filters/stringreplacer/stringreplacerplugin.cpp
@@ -0,0 +1,32 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generating the factories so String Replacer Filter can be used as plug in.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#include <kgenericfactory.h>
+
+#include "stringreplacerconf.h"
+#include "stringreplacerproc.h"
+
+typedef K_TYPELIST_2( StringReplacerProc, StringReplacerConf ) StringReplacerPlugin;
+K_EXPORT_COMPONENT_FACTORY( libkttsd_stringreplacerplugin,
+ KGenericFactory<StringReplacerPlugin>("kttsd_stringreplacer") )
+
diff --git a/kttsd/filters/stringreplacer/stringreplacerproc.cpp b/kttsd/filters/stringreplacer/stringreplacerproc.cpp
new file mode 100644
index 0000000..1b03810
--- /dev/null
+++ b/kttsd/filters/stringreplacer/stringreplacerproc.cpp
@@ -0,0 +1,231 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic String Replacement Filter Processing class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qdom.h>
+#include <qfile.h>
+#include <qlistview.h>
+
+// KDE includes.
+#include <kdebug.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+// KTTS includes.
+#include "filterproc.h"
+#include "talkercode.h"
+
+// StringReplacer includes.
+#include "stringreplacerproc.h"
+#include "stringreplacerproc.moc"
+
+/**
+ * Constructor.
+ */
+StringReplacerProc::StringReplacerProc( QObject *parent, const char *name, const QStringList& ) :
+ KttsFilterProc(parent, name)
+{
+}
+
+/**
+ * Destructor.
+ */
+/*virtual*/ StringReplacerProc::~StringReplacerProc()
+{
+ m_matchList.clear();
+ m_caseList.clear();
+ m_substList.clear();
+}
+
+/**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+bool StringReplacerProc::init(KConfig* config, const QString& configGroup){
+ // kdDebug() << "StringReplacerProc::init: Running" << endl;
+ QString wordsFilename =
+ KGlobal::dirs()->saveLocation( "data" ,"kttsd/stringreplacer/", false );
+ if ( wordsFilename.isEmpty() ) return false;
+ wordsFilename += configGroup;
+ config->setGroup( configGroup );
+ wordsFilename = config->readEntry( "WordListFile", wordsFilename );
+
+ // Open existing word list.
+ QFile file( wordsFilename );
+ if ( !file.open( IO_ReadOnly ) )
+ return false;
+ QDomDocument doc( "" );
+ if ( !doc.setContent( &file ) ) {
+ file.close();
+ return false;
+ }
+ file.close();
+
+ // Clear list.
+ m_matchList.clear();
+ m_caseList.clear();
+ m_substList.clear();
+
+ // Name setting.
+ // QDomNodeList nameList = doc.elementsByTagName( "name" );
+ // QDomNode nameNode = nameList.item( 0 );
+ // m_widget->nameLineEdit->setText( nameNode.toElement().text() );
+
+ // Language Codes setting. List may be single element of comma-separated values,
+ // or multiple elements.
+ m_languageCodeList.clear();
+ QDomNodeList languageList = doc.elementsByTagName( "language-code" );
+ for ( uint ndx=0; ndx < languageList.count(); ++ndx )
+ {
+ QDomNode languageNode = languageList.item( ndx );
+ m_languageCodeList += QStringList::split(',', languageNode.toElement().text(), false);
+ }
+
+ // AppId. Apply this filter only if DCOP appId of application that queued
+ // the text contains this string. List may be single element of comma-separated values,
+ // or multiple elements.
+ m_appIdList.clear();
+ QDomNodeList appIdList = doc.elementsByTagName( "appid" );
+ for ( uint ndx=0; ndx < appIdList.count(); ++ndx )
+ {
+ QDomNode appIdNode = appIdList.item( ndx );
+ m_appIdList += QStringList::split(',', appIdNode.toElement().text(), false);
+ }
+
+ // Word list.
+ QDomNodeList wordList = doc.elementsByTagName("word");
+ const int wordListCount = wordList.count();
+ for (int wordIndex = 0; wordIndex < wordListCount; ++wordIndex)
+ {
+ QDomNode wordNode = wordList.item(wordIndex);
+ QDomNodeList propList = wordNode.childNodes();
+ QString wordType;
+ QString matchCase = "No"; // Default for old (v<=3.5.3) config files with no <case/>.
+ QString match;
+ QString subst;
+ const int propListCount = propList.count();
+ for (int propIndex = 0; propIndex < propListCount; ++propIndex)
+ {
+ QDomNode propNode = propList.item(propIndex);
+ QDomElement prop = propNode.toElement();
+ if (prop.tagName() == "type") wordType = prop.text();
+ if (prop.tagName() == "case") matchCase = prop.text();
+ if (prop.tagName() == "match") match = prop.text();
+ if (prop.tagName() == "subst") subst = prop.text();
+ }
+ // Build Regular Expression for each word's match string.
+ QRegExp rx;
+ rx.setCaseSensitive(matchCase == "Yes");
+ if ( wordType == "Word" )
+ {
+ // TODO: Does \b honor strange non-Latin1 encodings?
+ rx.setPattern( "\\b" + match + "\\b" );
+ }
+ else
+ {
+ rx.setPattern( match );
+ }
+ // Add Regular Expression to list (if valid).
+ if ( rx.isValid() )
+ {
+ m_matchList.append( rx );
+ m_substList.append( subst );
+ }
+ }
+ return true;
+}
+
+/**
+ * Convert input, returning output.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+/*virtual*/ QString StringReplacerProc::convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId)
+{
+ m_wasModified = false;
+ // If language doesn't match, return input unmolested.
+ if ( !m_languageCodeList.isEmpty() )
+ {
+ QString languageCode = talkerCode->languageCode();
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText <<
+ // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
+ if ( !m_languageCodeList.contains( languageCode ) )
+ {
+ if ( !talkerCode->countryCode().isEmpty() )
+ {
+ languageCode += '_' + talkerCode->countryCode();
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText <<
+ // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
+ if ( !m_languageCodeList.contains( languageCode ) ) return inputText;
+ } else return inputText;
+ }
+ }
+ // If appId doesn't match, return input unmolested.
+ if ( !m_appIdList.isEmpty() )
+ {
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText << " if appId "
+ // << appId << " matches " << m_appIdList << endl;
+ bool found = false;
+ QString appIdStr = appId;
+ for ( uint ndx=0; ndx < m_appIdList.count(); ++ndx )
+ {
+ if ( appIdStr.contains(m_appIdList[ndx]) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ {
+ // kdDebug() << "StringReplacerProc::convert: appId not found" << endl;
+ return inputText;
+ }
+ }
+ QString newText = inputText;
+ const int listCount = m_matchList.count();
+ for ( int index = 0; index < listCount; ++index )
+ {
+ //kdDebug() << "newtext = " << newText << " matching " << m_matchList[index].pattern() << " replacing with " << m_substList[index] << endl;
+ newText.replace( m_matchList[index], m_substList[index] );
+ }
+ m_wasModified = true;
+ return newText;
+}
+
+/**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+/*virtual*/ bool StringReplacerProc::wasModified() { return m_wasModified; }
+
diff --git a/kttsd/filters/stringreplacer/stringreplacerproc.h b/kttsd/filters/stringreplacer/stringreplacerproc.h
new file mode 100644
index 0000000..114ebaf
--- /dev/null
+++ b/kttsd/filters/stringreplacer/stringreplacerproc.h
@@ -0,0 +1,97 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic String Replacement Filter Processing class.
+ This is the interface definition for text filters.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _STRINGREPLACERPROC_H_
+#define _STRINGREPLACERPROC_H_
+
+// Qt includes.
+#include <qobject.h>
+#include <qtextstream.h>
+#include <qvaluelist.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+
+// KTTS includes.
+#include "filterproc.h"
+
+class StringReplacerProc : virtual public KttsFilterProc
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor.
+ */
+ StringReplacerProc( QObject *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor.
+ */
+ virtual ~StringReplacerProc();
+
+ /**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+ virtual bool init(KConfig *config, const QString &configGroup);
+
+ /**
+ * Convert input, returning output.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+ virtual QString convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId);
+
+ /**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+ virtual bool wasModified();
+
+private:
+ // Language codes supported by the filter.
+ QStringList m_languageCodeList;
+ // If not empty, apply filter only to apps containing one or more of these strings.
+ QStringList m_appIdList;
+
+ // List of regular expressions to match.
+ QValueList<QRegExp> m_matchList;
+ // List of match case flag
+ QValueList<QString> m_caseList;
+ // List of substitutions to replace matches.
+ QValueList<QString> m_substList;
+ // True if this filter did anything to the text.
+ bool m_wasModified;
+};
+
+#endif // _STRINGREPLACERPROC_H_
diff --git a/kttsd/filters/talkerchooser/Makefile.am b/kttsd/filters/talkerchooser/Makefile.am
new file mode 100644
index 0000000..66f857d
--- /dev/null
+++ b/kttsd/filters/talkerchooser/Makefile.am
@@ -0,0 +1,25 @@
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkttsd_talkerchooserplugin.la
+
+libkttsd_talkerchooserplugin_la_SOURCES = \
+ talkerchooserconfwidget.ui \
+ talkerchooserconf.cpp \
+ talkerchooserproc.cpp \
+ talkerchooserplugin.cpp
+libkttsd_talkerchooserplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkttsd_talkerchooserplugin_la_LIBADD = $(top_builddir)/kttsd/libkttsd/libkttsd.la
+
+services_DATA = kttsd_talkerchooserplugin.desktop
+servicesdir = $(kde_servicesdir)
+
+# Install data files.
+talkerchooserdatadir = $(kde_datadir)/kttsd/talkerchooser/
+talkerchooserdata_DATA = \
+ female_notifications_rc
+
+noinst_HEADERS = talkerchooserconfwidget.h
diff --git a/kttsd/filters/talkerchooser/female_notifications_rc b/kttsd/filters/talkerchooser/female_notifications_rc
new file mode 100644
index 0000000..1cbd94c
--- /dev/null
+++ b/kttsd/filters/talkerchooser/female_notifications_rc
@@ -0,0 +1,10 @@
+[Filter]
+AppIDs=knotify
+Gender=*female
+LanguageCode=
+MatchRegExp=
+Rate=
+SynthName=
+TalkerCode=<voice gender="*female" />
+UserFilterName=Obsolete. Use Notifications tab instead.
+Volume=
diff --git a/kttsd/filters/talkerchooser/kttsd_talkerchooserplugin.desktop b/kttsd/filters/talkerchooser/kttsd_talkerchooserplugin.desktop
new file mode 100644
index 0000000..cdf1e45
--- /dev/null
+++ b/kttsd/filters/talkerchooser/kttsd_talkerchooserplugin.desktop
@@ -0,0 +1,89 @@
+[Desktop Entry]
+Name=Talker Chooser
+Name[bg]=Избор на глас
+Name[ca]=Selector de parla
+Name[cs]=Výběr hlasu
+Name[da]=Vælger af oplæser
+Name[de]=Sprecherauswahl
+Name[el]=Επιλογή εκφωνητή
+Name[es]=Selector de lectores
+Name[et]=Kõneleja valimine
+Name[fa]=انتخاب‌کنندۀ گوینده
+Name[fi]=Puhujan valitsin
+Name[fr]=Sélecteur de voix
+Name[ga]=Roghnóir Cainteora
+Name[gl]=Escolla de Locutor
+Name[hu]=Hangválasztó
+Name[is]=Talveljari
+Name[it]=Selezione dell'oratore
+Name[ja]=話者選択
+Name[ka]=მოლაპარაკის ამომრჩეველი
+Name[km]=ឧបករណ៍​ជ្រើស​អ្នក​និយាយ
+Name[mk]=Избирач на говорникот
+Name[ms]=Pemilih Penutur
+Name[nb]=Snakkervelger
+Name[nds]=Snacker-Utwahl
+Name[ne]=वक्ता चयनकर्ता
+Name[nl]=Spreker kiezen
+Name[pa]=ਬੋਲਚਾਲ ਚੋਣਕਾਰ
+Name[pl]=Wybieranie Mówców
+Name[pt]=Escolha de Orador
+Name[pt_BR]=Escolha do Talker
+Name[ru]=Диктор
+Name[sk]=Voľba hovorcu
+Name[sl]=Izbirnik govornika
+Name[sr]=Бирач говорника
+Name[sr@Latn]=Birač govornika
+Name[sv]=Urval av talare
+Name[ta]=டாக்கர் தேர்ந்தெடுப்பான்
+Name[tg]=Диктор
+Name[tr]=Konuşmacı Seçici
+Name[uk]=Вибір диктора
+Name[vi]=Trình chọn Máy nói
+Name[zh_TW]=語音選擇器
+Comment=Generic Talker Chooser Filter Plugin for KTTS
+Comment[bg]=Приставка за избор на глас за KTTS
+Comment[ca]=Connector del filtre selector genèric de parla pel KTTS
+Comment[cs]=Modul filtru výběru hlasu KTTS
+Comment[da]=Generisk vælger af oplæser for KTTS
+Comment[de]=Generischer Sprecherauswahlfilter für KTTS
+Comment[el]=Γενικό φίλτρο πρόσθετο επιλογής εκφωνητή για το KTTS
+Comment[es]=Complemento de audio para KTTSD
+Comment[et]=KTTS-i üldine kõneleja valimise filtri plugin
+Comment[eu]=KTTS-ren hizlariak hautatzeko iragazk-plugin generikoa
+Comment[fa]=وصلۀ پالایۀ انتخاب‌کنندۀ گویندۀ عمومی برای KTTS
+Comment[fi]=Yleinen puhujan valitsin suodatin liitännäinen KTTS-ohjelmalle
+Comment[fr]=Module de filtrage de sélection de voix pour KTTS
+Comment[gl]=Plugin para Escoller o Locutor para KTTS
+Comment[hu]=Általános hangkiválasztó modul a KTTS-hez
+Comment[is]=Almennt talvalsíforrit fyrir KTTS
+Comment[it]=Plugin generico per il filtro di scelta dell'oratore per KTTS
+Comment[ja]=KTTS 用汎用話者選択フィルタプラグイン
+Comment[ka]=მოლაპარაკის ამომრჩეველის ზოგადი ფილტრის მოდული KTTS-სთვის
+Comment[km]=កម្មវិធី​ជំនួយ​ឧបករណ៍​ជ្រើស​រើស​អ្នក​និយាយ​ទូទៅ​សម្រាប់ KTTS
+Comment[mk]=Филтер за KTTS за избирач на општ говорник
+Comment[ms]=Plugin Penapis Pemilih Penutur Generik bagi KTTS
+Comment[nb]=Filter-programtillegg til KTTS for snakkervalg
+Comment[nds]=KTTS-Filtermoduul för dat Utsöken vun den Snacker
+Comment[ne]=KTTS का लागि जेनेरीक वक्ता चयनकर्ता फिल्टर प्लगइन
+Comment[nl]=Generieke plugin om de spreker te kiezen voor KTTS
+Comment[pa]=KTTS ਲਈ ਸਧਾਰਨ ਬੋਲੀ ਚੋਣਕਾਰ ਫਿਲਟਰ ਪਲੱਗਿੰਨ
+Comment[pl]=Wtyczka wyboru Mówców dla KTTS
+Comment[pt]='Plugin' Genérico de Escolha de Orador para o KTTS
+Comment[pt_BR]=Plug-in de Filtro Genérico Para Escolha do Talker do KTTS
+Comment[ru]=Фильтр выбора диктора для KTTS
+Comment[sk]=Všeobecný modul filra na voľbu hovorcu v KTTS
+Comment[sl]=Filtrni vstavek KTTS za generični izbirnik govorca
+Comment[sr]=Генерички филтерски прикључак KTTS-а за бирач говорника
+Comment[sr@Latn]=Generički filterski priključak KTTS-a za birač govornika
+Comment[sv]=Insticksprogram för KTTS med generellt urvalsfilter för talare
+Comment[ta]=KTTS க்கான ஜெனரிக் பேச்சாளர் தேர்ந்தெடுப்பான அலங்கார சொருகுப்பொருள்
+Comment[tg]=Филтри интихоби диктор барои KTTS
+Comment[tr]=KTTS için Genel Konuşma Seçici Süzgeci Eklentisi
+Comment[uk]=Загальний втулок Фільтра вибору диктора для KTTS
+Comment[vi]=Trình bổ sung Lọc Chọn Máy nói Chung cho KTTS
+Comment[zh_TW]=KTTS 使用的語音選擇外掛程式
+Type=Service
+ServiceTypes=KTTSD/FilterPlugin
+X-KDE-Library=libkttsd_talkerchooserplugin
+X-KDE-Languages=en,en_US,en_GB,en_CA,es,es_mx,cy,de,fi,cs,pl
diff --git a/kttsd/filters/talkerchooser/talkerchooserconf.cpp b/kttsd/filters/talkerchooser/talkerchooserconf.cpp
new file mode 100644
index 0000000..0dbf53f
--- /dev/null
+++ b/kttsd/filters/talkerchooser/talkerchooserconf.cpp
@@ -0,0 +1,269 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic Talker Chooser Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qstring.h>
+#include <qhbox.h>
+#include <qlayout.h>
+
+// KDE includes.
+#include <klocale.h>
+#include <klineedit.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+#include <kcombobox.h>
+#include <kpushbutton.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kregexpeditorinterface.h>
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+#include <kfiledialog.h>
+
+// KTTS includes.
+
+// TalkerChooser includes.
+#include "talkerchooserconf.h"
+#include "talkerchooserconf.moc"
+
+/**
+* Constructor
+*/
+TalkerChooserConf::TalkerChooserConf( QWidget *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterConf(parent, name)
+{
+ // kdDebug() << "TalkerChooserConf::TalkerChooserConf: Running" << endl;
+
+ // Create configuration widget.
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint(), "TalkerChooserConfigWidgetLayout");
+ layout->setAlignment (Qt::AlignTop);
+ m_widget = new TalkerChooserConfWidget(this, "TalkerChooserConfigWidget");
+ layout->addWidget(m_widget);
+
+ // Determine if kdeutils Regular Expression Editor is installed.
+ m_reEditorInstalled = !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty();
+ m_widget->reEditorButton->setEnabled(m_reEditorInstalled);
+
+ connect(m_widget->nameLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->reLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->reEditorButton, SIGNAL(clicked()),
+ this, SLOT(slotReEditorButton_clicked()));
+ connect(m_widget->appIdLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->talkerButton, SIGNAL(clicked()),
+ this, SLOT(slotTalkerButton_clicked()));
+
+ connect(m_widget->loadButton, SIGNAL(clicked()),
+ this, SLOT(slotLoadButton_clicked()));
+ connect(m_widget->saveButton, SIGNAL(clicked()),
+ this, SLOT(slotSaveButton_clicked()));
+ connect(m_widget->clearButton, SIGNAL(clicked()),
+ this, SLOT(slotClearButton_clicked()));
+
+ // Set up defaults.
+ defaults();
+}
+
+/**
+* Destructor.
+*/
+TalkerChooserConf::~TalkerChooserConf(){
+ // kdDebug() << "TalkerChooserConf::~TalkerChooserConf: Running" << endl;
+}
+
+/**
+* This method is invoked whenever the module should read its
+* configuration (most of the times from a config file) and update the
+* user interface. This happens when the user clicks the "Reset" button in
+* the control center, to undo all of his changes and restore the currently
+* valid settings. Note that kttsmgr calls this when the plugin is
+* loaded, so it not necessary to call it in your constructor.
+* The plugin should read its configuration from the specified group
+* in the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* loading your configuration.
+*/
+void TalkerChooserConf::load(KConfig* config, const QString& configGroup){
+ // kdDebug() << "TalkerChooserConf::load: Running" << endl;
+ config->setGroup( configGroup );
+ m_widget->nameLineEdit->setText( config->readEntry( "UserFilterName", m_widget->nameLineEdit->text() ) );
+ m_widget->reLineEdit->setText(
+ config->readEntry("MatchRegExp", m_widget->reLineEdit->text()) );
+ m_widget->appIdLineEdit->setText(
+ config->readEntry("AppIDs", m_widget->appIdLineEdit->text()) );
+
+ m_talkerCode = TalkerCode(config->readEntry("TalkerCode"), false);
+ // Legacy settings.
+ QString s = config->readEntry( "LanguageCode" );
+ if (!s.isEmpty()) m_talkerCode.setFullLanguageCode(s);
+ s = config->readEntry( "SynthInName" );
+ if (!s.isEmpty()) m_talkerCode.setPlugInName(s);
+ s = config->readEntry( "Gender" );
+ if (!s.isEmpty()) m_talkerCode.setGender(s);
+ s = config->readEntry( "Volume" );
+ if (!s.isEmpty()) m_talkerCode.setVolume(s);
+ s = config->readEntry( "Rate" );
+ if (!s.isEmpty()) m_talkerCode.setRate(s);
+
+ m_widget->talkerLineEdit->setText(m_talkerCode.getTranslatedDescription());
+}
+
+/**
+* This function gets called when the user wants to save the settings in
+* the user interface, updating the config files or wherever the
+* configuration is stored. The method is called when the user clicks "Apply"
+* or "Ok". The plugin should save its configuration in the specified
+* group of the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* saving your configuration.
+*/
+void TalkerChooserConf::save(KConfig* config, const QString& configGroup){
+ // kdDebug() << "TalkerChooserConf::save: Running" << endl;
+ config->setGroup( configGroup );
+ config->writeEntry( "UserFilterName", m_widget->nameLineEdit->text() );
+ config->writeEntry( "MatchRegExp", m_widget->reLineEdit->text() );
+ config->writeEntry( "AppIDs", m_widget->appIdLineEdit->text().replace(" ", "") );
+ config->writeEntry( "TalkerCode", m_talkerCode.getTalkerCode());
+}
+
+/**
+* This function is called to set the settings in the module to sensible
+* default values. It gets called when hitting the "Default" button. The
+* default values should probably be the same as the ones the application
+* uses when started without a config file. Note that defaults should
+* be applied to the on-screen widgets; not to the config file.
+*/
+void TalkerChooserConf::defaults(){
+ // kdDebug() << "TalkerChooserConf::defaults: Running" << endl;
+ // Default name.
+ m_widget->nameLineEdit->setText( i18n("Talker Chooser") );
+ // Default regular expression is blank.
+ m_widget->reLineEdit->setText( "" );
+ // Default App ID is blank.
+ m_widget->appIdLineEdit->setText( "" );
+ // Default to using default Talker.
+ m_talkerCode = TalkerCode( QString::null, false );
+ m_widget->talkerLineEdit->setText( m_talkerCode.getTranslatedDescription() );
+}
+
+/**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+bool TalkerChooserConf::supportsMultiInstance() { return true; }
+
+/**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be TalkerCode::translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+QString TalkerChooserConf::userPlugInName()
+{
+ if (m_widget->talkerLineEdit->text().isEmpty()) return QString::null;
+ if (m_widget->appIdLineEdit->text().isEmpty() &&
+ m_widget->reLineEdit->text().isEmpty()) return QString::null;
+ QString instName = m_widget->nameLineEdit->text();
+ if (instName.isEmpty()) return QString::null;
+ return instName;
+}
+
+void TalkerChooserConf::slotReEditorButton_clicked()
+{
+ // Show Regular Expression Editor dialog if it is installed.
+ if ( !m_reEditorInstalled ) return;
+ QDialog *editorDialog =
+ KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+ if ( editorDialog )
+ {
+ // kdeutils was installed, so the dialog was found. Fetch the editor interface.
+ KRegExpEditorInterface *reEditor =
+ static_cast<KRegExpEditorInterface *>(editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT( reEditor ); // This should not fail!// now use the editor.
+ reEditor->setRegExp( m_widget->reLineEdit->text() );
+ int dlgResult = editorDialog->exec();
+ if ( dlgResult == QDialog::Accepted )
+ {
+ QString re = reEditor->regExp();
+ m_widget->reLineEdit->setText( re );
+ }
+ delete editorDialog;
+ } else return;
+}
+
+void TalkerChooserConf::slotTalkerButton_clicked()
+{
+ QString talkerCode = m_talkerCode.getTalkerCode();
+ SelectTalkerDlg dlg( m_widget, "selecttalkerdialog", i18n("Select Talker"), talkerCode, true );
+ int dlgResult = dlg.exec();
+ if ( dlgResult != KDialogBase::Accepted ) return;
+ m_talkerCode = TalkerCode( dlg.getSelectedTalkerCode(), false );
+ m_widget->talkerLineEdit->setText( m_talkerCode.getTranslatedDescription() );
+ configChanged();
+}
+
+void TalkerChooserConf::slotLoadButton_clicked()
+{
+ QString dataDir = KGlobal::dirs()->findAllResources("data", "kttsd/talkerchooser/").last();
+ QString filename = KFileDialog::getOpenFileName(
+ dataDir,
+ "*rc|Talker Chooser Config (*rc)",
+ m_widget,
+ "talkerchooser_loadfile");
+ if ( filename.isEmpty() ) return;
+ KConfig* cfg = new KConfig( filename, true, false, 0 );
+ load( cfg, "Filter" );
+ delete cfg;
+ configChanged();
+}
+
+void TalkerChooserConf::slotSaveButton_clicked()
+{
+ QString filename = KFileDialog::getSaveFileName(
+ KGlobal::dirs()->saveLocation( "data" ,"kttsd/talkerchooser/", false ),
+ "*rc|Talker Chooser Config (*rc)",
+ m_widget,
+ "talkerchooser_savefile");
+ if ( filename.isEmpty() ) return;
+ KConfig* cfg = new KConfig( filename, false, false, 0 );
+ save( cfg, "Filter" );
+ delete cfg;
+}
+
+void TalkerChooserConf::slotClearButton_clicked()
+{
+ m_widget->nameLineEdit->setText( QString::null );
+ m_widget->reLineEdit->setText( QString::null );
+ m_widget->appIdLineEdit->setText( QString::null );
+ m_talkerCode = TalkerCode( QString::null, false );
+ m_widget->talkerLineEdit->setText( m_talkerCode.getTranslatedDescription() );
+ configChanged();
+}
diff --git a/kttsd/filters/talkerchooser/talkerchooserconf.h b/kttsd/filters/talkerchooser/talkerchooserconf.h
new file mode 100644
index 0000000..d6b3959
--- /dev/null
+++ b/kttsd/filters/talkerchooser/talkerchooserconf.h
@@ -0,0 +1,134 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic Talker Chooser Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _TALKERCHOOSERCONF_H_
+#define _TALKERCHOOSERCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "filterconf.h"
+#include "talkercode.h"
+#include "selecttalkerdlg.h"
+
+// TalkerChooser includes.
+#include "talkerchooserconfwidget.h"
+
+class KDialogBase;
+class EditReplacementWidget;
+
+class TalkerChooserConf : public KttsFilterConf
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ TalkerChooserConf( QWidget *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor
+ */
+ virtual ~TalkerChooserConf();
+
+ /**
+ * This method is invoked whenever the module should read its
+ * configuration (most of the times from a config file) and update the
+ * user interface. This happens when the user clicks the "Reset" button in
+ * the control center, to undo all of his changes and restore the currently
+ * valid settings. Note that KTTSMGR calls this when the plugin is
+ * loaded, so it not necessary to call it in your constructor.
+ * The plugin should read its configuration from the specified group
+ * in the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * loading your configuration.
+ *
+ * When a plugin is first added to KTTSMGR, @e load will be called with
+ * a Null @e configGroup. In this case, the plugin will not have
+ * any instance-specific parameters to load, but it may still wish
+ * to load parameters that apply to all instances of the plugin.
+ */
+ virtual void load(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function gets called when the user wants to save the settings in
+ * the user interface, updating the config files or wherever the
+ * configuration is stored. The method is called when the user clicks "Apply"
+ * or "Ok". The plugin should save its configuration in the specified
+ * group of the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * saving your configuration.
+ */
+ virtual void save(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function is called to set the settings in the module to sensible
+ * default values. It gets called when hitting the "Default" button. The
+ * default values should probably be the same as the ones the application
+ * uses when started without a config file. Note that defaults should
+ * be applied to the on-screen widgets; not to the config file.
+ */
+ virtual void defaults();
+
+ /**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+ virtual bool supportsMultiInstance();
+
+ /**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+ virtual QString userPlugInName();
+
+ private slots:
+ void slotReEditorButton_clicked();
+ void slotTalkerButton_clicked();
+ void slotLoadButton_clicked();
+ void slotSaveButton_clicked();
+ void slotClearButton_clicked();
+
+ private:
+ // Configuration Widget.
+ TalkerChooserConfWidget* m_widget;
+ // True if kdeutils Regular Expression Editor is installed.
+ bool m_reEditorInstalled;
+ // User's chosen Talker Code.
+ TalkerCode m_talkerCode;
+};
+
+#endif //_TALKERCHOOSERCONF_H_
diff --git a/kttsd/filters/talkerchooser/talkerchooserconfwidget.ui b/kttsd/filters/talkerchooser/talkerchooserconfwidget.ui
new file mode 100644
index 0000000..3cdee2e
--- /dev/null
+++ b/kttsd/filters/talkerchooser/talkerchooserconfwidget.ui
@@ -0,0 +1,319 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>TalkerChooserConfWidget</class>
+<comment>Widget for configuring Talker Chooser Filter for KTTSD.</comment>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TalkerChooserConfWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>547</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Configure Talker Chooser</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter any name you like for this filter.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter any name you like for this filter.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>applyGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Apply This Filter When</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These settings determines when the filter is applied to text.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>reLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Te&amp;xt contains:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>reLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank, the filter applies to all text jobs of any language.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>appIdLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Application &amp;ID contains:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>reLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank, the filter applies to all text jobs of any language.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>reEditorButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to select one or more languages. This filter will be applied to text jobs of those languages.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. Use &lt;b&gt;knotify&lt;/b&gt; to match all messages sent as KDE notifications. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout19_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>talkerLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Talker:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>talkerLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The new Talker that will be used when the conditions above are met. The default Talker is the topmost in the Talkers tab. Click the button to choose a Talker.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>talkerLineEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The new Talker that will be used when the conditions above are met. The default Talker is the topmost in the Talkers tab. Click the button to choose a Talker.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>talkerButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to select a Talker.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>loadButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Load...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to load a Talker Chooser configuration from a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>saveButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Save...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to save this Talker Chooser to a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>clearButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Cl&amp;ear</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to clear everything.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>nameLineEdit</tabstop>
+ <tabstop>reLineEdit</tabstop>
+ <tabstop>reEditorButton</tabstop>
+ <tabstop>appIdLineEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/filters/talkerchooser/talkerchooserplugin.cpp b/kttsd/filters/talkerchooser/talkerchooserplugin.cpp
new file mode 100644
index 0000000..4181f6a
--- /dev/null
+++ b/kttsd/filters/talkerchooser/talkerchooserplugin.cpp
@@ -0,0 +1,32 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generating the factories so Talker Chooser Filter can be used as plug in.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#include <kgenericfactory.h>
+
+#include "talkerchooserconf.h"
+#include "talkerchooserproc.h"
+
+typedef K_TYPELIST_2( TalkerChooserProc, TalkerChooserConf ) TalkerChooserPlugin;
+K_EXPORT_COMPONENT_FACTORY( libkttsd_talkerchooserplugin,
+ KGenericFactory<TalkerChooserPlugin>("kttsd_talkerchooser") )
+
diff --git a/kttsd/filters/talkerchooser/talkerchooserproc.cpp b/kttsd/filters/talkerchooser/talkerchooserproc.cpp
new file mode 100644
index 0000000..628b68a
--- /dev/null
+++ b/kttsd/filters/talkerchooser/talkerchooserproc.cpp
@@ -0,0 +1,148 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic Talker Chooser Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qregexp.h>
+
+// KDE includes.
+#include <kdebug.h>
+#include <kconfig.h>
+
+// KTTS includes.
+#include "talkercode.h"
+
+// Talker Chooser includes.
+#include "talkerchooserproc.h"
+#include "talkerchooserproc.moc"
+
+/**
+ * Constructor.
+ */
+TalkerChooserProc::TalkerChooserProc( QObject *parent, const char *name, const QStringList& /*args*/ ) :
+ KttsFilterProc(parent, name)
+{
+ // kdDebug() << "TalkerChooserProc::TalkerChooserProc: Running" << endl;
+}
+
+/**
+ * Destructor.
+ */
+TalkerChooserProc::~TalkerChooserProc()
+{
+ // kdDebug() << "TalkerChooserProc::~TalkerChooserProc: Running" << endl;
+}
+
+/**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+bool TalkerChooserProc::init(KConfig* config, const QString& configGroup){
+ // kdDebug() << "PlugInProc::init: Running" << endl;
+ config->setGroup( configGroup );
+ m_re = config->readEntry( "MatchRegExp" );
+ m_appIdList = config->readListEntry( "AppIDs" );
+ m_chosenTalkerCode = TalkerCode(config->readEntry("TalkerCode"), false);
+ // Legacy settings.
+ QString s = config->readEntry( "LanguageCode" );
+ if (!s.isEmpty()) m_chosenTalkerCode.setFullLanguageCode(s);
+ s = config->readEntry( "SynthInName" );
+ if (!s.isEmpty()) m_chosenTalkerCode.setPlugInName(s);
+ s = config->readEntry( "Gender" );
+ if (!s.isEmpty()) m_chosenTalkerCode.setGender(s);
+ s = config->readEntry( "Volume" );
+ if (!s.isEmpty()) m_chosenTalkerCode.setVolume(s);
+ s = config->readEntry( "Rate" );
+ if (!s.isEmpty()) m_chosenTalkerCode.setRate(s);
+ return true;
+}
+
+/**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+/*virtual*/ bool TalkerChooserProc::supportsAsync() { return false; }
+
+/**
+ * Convert input, returning output. Runs synchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+/*virtual*/ QString TalkerChooserProc::convert(const QString& inputText, TalkerCode* talkerCode,
+ const QCString& appId)
+{
+ if ( !m_re.isEmpty() )
+ {
+ int pos = inputText.find( QRegExp(m_re) );
+ if ( pos < 0 ) return inputText;
+ }
+ // If appId doesn't match, return input unmolested.
+ if ( !m_appIdList.isEmpty() )
+ {
+ // kdDebug() << "TalkerChooserProc::convert: converting " << inputText << " if appId "
+ // << appId << " matches " << m_appIdList << endl;
+ bool found = false;
+ QString appIdStr = appId;
+ for ( uint ndx=0; ndx < m_appIdList.count(); ++ndx )
+ {
+ if ( appIdStr.contains(m_appIdList[ndx]) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ {
+ // kdDebug() << "TalkerChooserProc::convert: appId not found" << endl;
+ return inputText;
+ }
+ }
+
+ // Set the talker.
+ // kdDebug() << "TalkerChooserProc::convert: setting lang " << m_languageCode <<
+ // " gender " << m_gender << " synth " << m_synth <<
+ // " volume " << m_volume << " rate " << m_rate << endl;
+ // Only override the language if user specified a language code.
+ if (!m_chosenTalkerCode.fullLanguageCode().isEmpty())
+ talkerCode->setFullLanguageCode(m_chosenTalkerCode.fullLanguageCode());
+ talkerCode->setVoice(m_chosenTalkerCode.voice());
+ talkerCode->setGender(m_chosenTalkerCode.gender());
+ talkerCode->setPlugInName(m_chosenTalkerCode.plugInName());
+ talkerCode->setVolume(m_chosenTalkerCode.volume());
+ talkerCode->setRate(m_chosenTalkerCode.rate());
+ return inputText;
+}
diff --git a/kttsd/filters/talkerchooser/talkerchooserproc.h b/kttsd/filters/talkerchooser/talkerchooserproc.h
new file mode 100644
index 0000000..07cde68
--- /dev/null
+++ b/kttsd/filters/talkerchooser/talkerchooserproc.h
@@ -0,0 +1,87 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic Talker Chooser Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _TALKERCHOOSERPROC_H_
+#define _TALKERCHOOSERPROC_H_
+
+// KTTS includes.
+#include "filterproc.h"
+#include "talkercode.h"
+
+class TalkerChooserProc : virtual public KttsFilterProc
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor.
+ */
+ TalkerChooserProc( QObject *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor.
+ */
+ virtual ~TalkerChooserProc();
+
+ /**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+ virtual bool init(KConfig *config, const QString &configGroup);
+
+ /**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+ virtual bool supportsAsync();
+
+ /**
+ * Convert input, returning output. Runs synchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+ virtual QString convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId);
+
+private:
+
+ QString m_re;
+ QStringList m_appIdList;
+ TalkerCode m_chosenTalkerCode;
+};
+
+#endif // _TALKERCHOOSERPROC_H_
diff --git a/kttsd/filters/xhtml2ssml/Doxyfile b/kttsd/filters/xhtml2ssml/Doxyfile
new file mode 100644
index 0000000..0061358
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/Doxyfile
@@ -0,0 +1,266 @@
+# Doxyfile 1.3.7-KDevelop
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = xhtml2ssml.kdevelop
+PROJECT_NUMBER = $VERSION$
+OUTPUT_DIRECTORY =
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+USE_WINDOWS_ENCODING = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = /home/paul/
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP = NO
+INHERIT_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+SUBGROUPING = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = /home/paul/Kdecvs/kdeaccessibility/kttsd/filters
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.cs \
+ *.php \
+ *.php3 \
+ *.inc \
+ *.m \
+ *.mm \
+ *.C \
+ *.CC \
+ *.C++ \
+ *.II \
+ *.I++ \
+ *.H \
+ *.HH \
+ *.H++ \
+ *.CS \
+ *.PHP \
+ *.PHP3 \
+ *.M \
+ *.MM \
+ *.C \
+ *.H \
+ *.tlh \
+ *.diff \
+ *.patch \
+ *.moc \
+ *.xpm \
+ *.dox
+RECURSIVE = yes
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = yes
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE = xhtml2ssml.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+UML_LOOK = NO
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 1000
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/kttsd/filters/xhtml2ssml/demonstration.html b/kttsd/filters/xhtml2ssml/demonstration.html
new file mode 100644
index 0000000..89bf8a3
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/demonstration.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ <p>Isn't it such a nice and <strong>bold</strong> day today?</p>
+ </body>
+</html>
diff --git a/kttsd/filters/xhtml2ssml/main.cpp b/kttsd/filters/xhtml2ssml/main.cpp
new file mode 100644
index 0000000..822d068
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/main.cpp
@@ -0,0 +1,22 @@
+
+#include <qapplication.h>
+#include <qfile.h>
+#include <qxml.h>
+#include <qmap.h>
+#include <iostream>
+#include "xhtml2ssml.h"
+#include "xmlelement.h"
+
+int main(int argc, char *argv[]) {
+ QApplication a(argc, argv);
+ QFile f("demonstration.html");
+ QXmlInputSource input(&f);
+ QXmlSimpleReader reader;
+ XHTMLToSSMLParser *parser = new XHTMLToSSMLParser();
+ reader.setContentHandler(parser);
+ reader.parse(input);
+ std::cout << parser->convertedText() << "\n";
+ delete parser;
+ return 0;
+}
+
diff --git a/kttsd/filters/xhtml2ssml/tagmappingrc b/kttsd/filters/xhtml2ssml/tagmappingrc
new file mode 100644
index 0000000..425af8e
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/tagmappingrc
@@ -0,0 +1,5 @@
+# This is a comment
+# and another one
+# a html 'b' or 'strong' tag is mapped to 'emphasis level="strong"'
+b:emphasis level="strong"
+strong:emphasis level="strong"
diff --git a/kttsd/filters/xhtml2ssml/xhtml2ssml.cpp b/kttsd/filters/xhtml2ssml/xhtml2ssml.cpp
new file mode 100644
index 0000000..7c77b9e
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xhtml2ssml.cpp
@@ -0,0 +1,105 @@
+
+
+/****************************************************************************
+ XHTMLToSSMLParser class
+
+ Parses a piece of XHTML markup and converts into SSML.
+ -------------------
+ Copyright:
+ (C) 2004 by Paul Giannaros <ceruleanblaze@gmail.com>
+ -------------------
+ Original author: Paul Giannaros <ceruleanblaze@gmail.com>
+******************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include <qstring.h>
+#include <qdict.h>
+#include <qxml.h>
+#include <qfile.h>
+#include <iostream>
+
+#include "xmlelement.h"
+#include "xhtml2ssml.h"
+
+
+/// Document parsing begin. Init stuff here.
+bool XHTMLToSSMLParser::startDocument() {
+ /// Read the file which maps xhtml tags -> ssml tags. Look at the file for more information.
+ QFile file("tagmappingrc");
+ if(!file.open(IO_ReadOnly)) {
+ std::cerr << "Could not read config file 'tagmappingrc'. Please check that it exists and is readable.\n";
+ // Kill further parsing
+ return false;
+ }
+ QTextStream stream(&file);
+ // File parsing.
+ bool linestatus = true;
+ while(!stream.atEnd()) {
+ linestatus = readFileConfigEntry(stream.readLine());
+ // If there's some syntactical error in the file then return false.
+ if(!linestatus)
+ return false;
+ /// Maybe call processEvents() to prevent GUI blockages?
+ }
+ return true;
+}
+
+bool XHTMLToSSMLParser::startElement(const QString &, const QString &, const QString &qName, const QXmlAttributes &atts) {
+ QString attributes = "";
+ if(atts.length() > 0) {
+ const int attsLength = atts.lenght();
+ for(int i = 0; i < attsLength; ++i)
+ attributes += " " + atts.qName(i) + "=\"" + atts.value(i) + "\"";
+ }
+ QString fromelement = qName + attributes;
+ // If this element is one of the keys that was specified in the configuration file, get what it should be converted to and
+ // append to the output string.
+ QString toelement = m_xhtml2ssml[fromelement];
+ if(toelement)
+ m_output.append(XMLElement::fromQString(toelement).startTag());
+ return true;
+}
+
+bool XHTMLToSSMLParser::endElement(const QString &, const QString &, const QString &qName) {
+ QString fromelement = qName;
+ QString toelement = m_xhtml2ssml[fromelement];
+ if(toelement)
+ m_output.append(XMLElement::fromQString(toelement).endTag());
+ return true;
+}
+
+bool XHTMLToSSMLParser::characters(const QString &characters) {
+ m_output.append(characters);
+ return true;
+}
+
+
+QString XHTMLToSSMLParser::convertedText() {
+ return m_output.simplifyWhiteSpace();
+}
+
+/// Parse a line from the configuration file which maps xhtml : ssml equivalent.
+/// It makes entries in the m_xhtml2ssml map accordingly.
+/// @param line A line from a file to parse
+/// @returns true if the syntax of the line was okay and the parsing succeeded - false otherwise.
+bool XHTMLToSSMLParser::readFileConfigEntry(const QString &line) {
+ // comments
+ if(line.stripWhiteSpace().startsWith("#")) {
+ return true;
+ }
+ // break into QStringList
+ // the second parameter to split is the string, with all space simplified and all space around the : removed, i.e
+ // "something : somethingelse" -> "something:somethingelse"
+ QStringList keyvalue = QStringList::split(":", line.simplifyWhiteSpace().replace(" :", ":").replace(": ", ":"));
+ if(keyvalue.count() != 2)
+ return false;
+ m_xhtml2ssml[keyvalue[0]] = keyvalue[1];
+ return true;
+}
diff --git a/kttsd/filters/xhtml2ssml/xhtml2ssml.h b/kttsd/filters/xhtml2ssml/xhtml2ssml.h
new file mode 100644
index 0000000..7271dc0
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xhtml2ssml.h
@@ -0,0 +1,61 @@
+
+/****************************************************************************
+ XHTMLToSSMLParser class
+
+ Parses a piece of XHTML markup and converts into SSML.
+ -------------------
+ Copyright:
+ (C) 2004 by Paul Giannaros <ceruleanblaze@gmail.com>
+ -------------------
+ Original author: Paul Giannaros <ceruleanblaze@gmail.com>
+******************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef _XHTML2SSML_H_
+#define _XHTML2SSML_H_
+
+#include <qxml.h>
+#include <qmap.h>
+
+typedef QMap<QString, QString> QStringMap;
+class QString;
+
+class XHTMLToSSMLParser : public QXmlDefaultHandler {
+
+public:
+ /// No need to reimplement constructor..
+ /// The document parsing starts
+ bool startDocument();
+ /// start of an element encountered (<element foo="bar">)
+ bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts);
+ /// end of an element encountered (</element>)
+ bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName);
+ /// text encountered (blah bah blah)
+ bool characters(const QString &);
+
+ /// Get the output text that was generated during the parsing.
+ /// @returns The converted text.
+ QString convertedText();
+
+ /// Parse a line from the configuration file which maps xhtml : ssml equivalent.
+ /// It makes entries in the m_xhtml2ssml map accordingly.
+ /// @param line A line from a file to parse
+ /// @returns true if the syntax of the line was okay and the parsing succeeded - false otherwise.
+ bool readFileConfigEntry(const QString &line);
+
+private:
+ /// Dict of xhtml tags -> ssml tags
+ QStringMap m_xhtml2ssml;
+ /// The output of the conversion
+ QString m_output;
+};
+
+#endif
diff --git a/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop b/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop
new file mode 100644
index 0000000..691735f
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop
@@ -0,0 +1,112 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Paul Giannaros</author>
+ <email>ceruleanblaze@gmail.com</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevTrollProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <keywords>
+ <keyword>Qt</keyword>
+ </keywords>
+ </general>
+ <kdevfileview>
+ <groups>
+ <group pattern="*.cpp;*.cxx;*.h" name="Sources" />
+ <group pattern="*.ui" name="User Interface" />
+ <group pattern="*.png" name="Icons" />
+ <group pattern="*" name="Others" />
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>stl</toc>
+ <toc>w3c-svg</toc>
+ <toc>sw</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ </general>
+ </kdevdebugger>
+ <kdevfilecreate>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevtrollproject>
+ <general>
+ <activedir></activedir>
+ </general>
+ <run>
+ <directoryradio>executable</directoryradio>
+ </run>
+ </kdevtrollproject>
+ <kdevcppsupport>
+ <references>
+ <pcs>kdeincludedb</pcs>
+ <pcs>qtincludedb</pcs>
+ <pcs>qtincludesdb</pcs>
+ <pcs>kgmailtoincludedb</pcs>
+ </references>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ </kdevcppsupport>
+ <kdevcvsservice>
+ <recursivewhenupdate>true</recursivewhenupdate>
+ <prunedirswhenupdate>true</prunedirswhenupdate>
+ <createdirswhenupdate>true</createdirswhenupdate>
+ <recursivewhencommitremove>true</recursivewhencommitremove>
+ <revertoptions>-C</revertoptions>
+ </kdevcvsservice>
+</kdevelop>
diff --git a/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop.pcs b/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop.pcs
new file mode 100644
index 0000000..4cdb884
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevelop.pcs
Binary files differ
diff --git a/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevses b/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevses
new file mode 100644
index 0000000..7128bc6
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xhtml2ssml.kdevses
@@ -0,0 +1,38 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<!DOCTYPE KDevPrjSession>
+<KDevPrjSession>
+ <DocsAndViews NumberOfDocuments="5" >
+ <Doc0 NumberOfViews="1" URL="file:/home/paul/Kdecvs/kdeaccessibility/kttsd/filters/xhtml2ssml.cpp" >
+ <View0 line="63" Type="Source" />
+ </Doc0>
+ <Doc1 NumberOfViews="1" URL="file:/home/paul/Kdecvs/kdeaccessibility/kttsd/filters/xhtml2ssml.h" >
+ <View0 line="21" Type="Source" />
+ </Doc1>
+ <Doc2 NumberOfViews="1" URL="file:/home/paul/Kdecvs/kdeaccessibility/kttsd/filters/main.cpp" >
+ <View0 line="17" Type="Source" />
+ </Doc2>
+ <Doc3 NumberOfViews="1" URL="file:/home/paul/Kdecvs/kdeaccessibility/kttsd/filters/xmlelement.cpp" >
+ <View0 line="72" Type="Source" />
+ </Doc3>
+ <Doc4 NumberOfViews="1" URL="file:/home/paul/Kdecvs/kdeaccessibility/kttsd/filters/xmlelement.h" >
+ <View0 line="75" Type="Source" />
+ </Doc4>
+ </DocsAndViews>
+ <pluginList>
+ <kdevbookmarks>
+ <bookmarks/>
+ </kdevbookmarks>
+ <kdevsubversion>
+ <subversion recurseresolve="1" recurserelocate="1" recursemerge="1" recursecommit="1" base="" recursepropget="1" recurseswitch="1" recurseupdate="1" recursepropset="1" recursediff="1" recurserevert="1" forcemove="1" recursecheckout="1" forceremove="1" recurseadd="1" recurseproplist="1" forcemerge="1" />
+ </kdevsubversion>
+ <kdevvalgrind>
+ <executable path="" params="" />
+ <valgrind path="" params="" />
+ <calltree path="" params="" />
+ <kcachegrind path="" />
+ </kdevvalgrind>
+ <kdevdebugger>
+ <breakpointList/>
+ </kdevdebugger>
+ </pluginList>
+</KDevPrjSession>
diff --git a/kttsd/filters/xhtml2ssml/xhtml2ssml.pro b/kttsd/filters/xhtml2ssml/xhtml2ssml.pro
new file mode 100644
index 0000000..a9b5a4a
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xhtml2ssml.pro
@@ -0,0 +1,10 @@
+######################################################################
+# Automatically generated by qmake (1.07a) Tue Dec 21 21:18:47 2004
+######################################################################
+
+TEMPLATE = app
+INCLUDEPATH += .
+
+# Input
+HEADERS += xhtml2ssml.h xmlelement.h
+SOURCES += main.cpp xhtml2ssml.cpp xmlelement.cpp
diff --git a/kttsd/filters/xhtml2ssml/xmlelement.cpp b/kttsd/filters/xhtml2ssml/xmlelement.cpp
new file mode 100644
index 0000000..53fa4f6
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xmlelement.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+ XMLElement class
+
+ Representation of an XML element with methods for getting/setting
+ attributes and generating "opening" and "closing" tags.
+ -------------------
+ Copyright:
+ (C) 2004 by Paul Giannaros <ceruleanblaze@gmail.com>
+ -------------------
+ Original author: Paul Giannaros <ceruleanblaze@gmail.com>
+******************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "xmlelement.h"
+#include <qstringlist.h>
+#include <iostream>
+
+/// Constructors
+XMLElement::XMLElement() {
+ m_name = "";
+ m_attrmapper = AttributeToValueMap();
+}
+XMLElement::XMLElement(const QString &name) {
+ m_name = name;
+ m_attrmapper = AttributeToValueMap();
+}
+/// Destructor
+XMLElement::~XMLElement() {
+ return;
+}
+
+/// Copy constructor
+XMLElement::XMLElement(const XMLElement &element) {
+ m_attrmapper = element.m_attrmapper;
+ m_name = element.m_name;
+}
+
+/// Assignement operator
+XMLElement XMLElement::operator=(const XMLElement &element) {
+ m_attrmapper = element.m_attrmapper;
+ m_name = element.m_name;
+ return *this;
+}
+
+QString XMLElement::name() {
+ return m_name;
+}
+QString XMLElement::startTag() {
+ QString output = "<" + m_name + " ";
+ for(AttributeToValueMap::Iterator it = m_attrmapper.begin(); it != m_attrmapper.end(); ++it) {
+ output.append(it.key() + "=\"" + it.data() + "\" ");
+ }
+ output = output.left(output.length() - 1);
+ // Get rid of the space at the end and then append a '>'
+ output.append(">");
+ return output;
+}
+
+QString XMLElement::endTag() {
+ return "</" + m_name + ">";
+}
+
+void XMLElement::setAttribute(const QString &attr, const QString &value) {
+ m_attrmapper[attr] = value;
+}
+QString XMLElement::attribute(const QString &attr) {
+ return m_attrmapper[attr];
+}
+
+QString XMLElement::toQString() {
+ QString tag = startTag();
+ return tag.left(tag.length() - 1).right(tag.length() - 2);
+}
+
+XMLElement XMLElement::fromQString(const QString &str) {
+ QStringList sections = QStringList::split(" ", str);
+ QString tagname = sections[0];
+ XMLElement e(tagname.latin1());
+
+ sections.pop_front();
+ // Loop over the remaining strings which are attributes="values"
+ if(sections.count()) {
+ const int sectionsCount = sections.count();
+ for(int i = 0; i < sectionsCount; ++i) {
+ QStringList list = QStringList::split("=", sections[i]);
+ if(list.count() != 2) {
+ std::cerr << "XMLElement::fromQString: Cannot convert list: " << list.join("|") << ". `" << str << "' is not in valid format.\n";
+ return XMLElement(" ");
+ }
+ e.setAttribute(list[0], list[1].left(list[1].length() - 1).right(list[1].length() -2));
+ }
+ }
+ return e;
+}
+
diff --git a/kttsd/filters/xhtml2ssml/xmlelement.h b/kttsd/filters/xhtml2ssml/xmlelement.h
new file mode 100644
index 0000000..c78cd6f
--- /dev/null
+++ b/kttsd/filters/xhtml2ssml/xmlelement.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+ XMLElement class
+
+ Representation of an XML element with methods for getting/setting
+ attributes and generating "opening" and "closing" tags.
+ -------------------
+ Copyright:
+ (C) 2004 by Paul Giannaros <ceruleanblaze@gmail.com>
+ -------------------
+ Original author: Paul Giannaros <ceruleanblaze@gmail.com>
+******************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef XMLELEMENT_H
+#define XMLELEMENT_H
+
+#include <qmap.h>
+
+class QString;
+
+typedef QMap<QString, QString> AttributeToValueMap;
+
+class XMLElement {
+public:
+ XMLElement();
+ XMLElement(const QString &name);
+ ~XMLElement();
+
+ /// Copy constructor
+ XMLElement(const XMLElement &);
+
+ /// Assignment operator
+ XMLElement operator=(const XMLElement &element);
+
+ /// Get the name of the tag (the text between the greater than and less than symbols).
+ /// @returns the name of the tag.
+ QString name();
+
+ /// set the name of the tag.
+ /// @param name the new name of the tag.
+ void setName(const QString &name);
+
+ /// Get a textual representation of the starting of the tag with all attributes and their values set out.
+ /// @verbatim
+ /// XMLElement element("elem");
+ /// element.addAttribute("foo", "bar");
+ /// element.startTag(); <- <elem foo="bar">
+ /// @endverbatim
+ /// @returns A textual representation of the start of the element.
+ QString startTag();
+
+ /// Get a textual representation of the closed tag that XMLElement represents.
+ /// @returns A textual representation of the closed tag represented by the XMLElement.
+ QString endTag();
+
+ /// Create an attribute and set its value.
+ /// @param attr The attribute.
+ /// @param value The value of the attribute.
+ void setAttribute(const QString &attr, const QString &value);
+
+ /// Get the value of an attribute.
+ /// @param attr The attribute.
+ /// @returns The value of @param attr
+ QString attribute(const QString &attr);
+
+ /// Convert to a QString.
+ /// Had issues with QMap and custom classes. For now you can just convert to/from QString and use
+ /// That as the key/value pair.
+ /// @returns A QString representation of the XMLAttribute.
+ QString toQString();
+
+ /// Create an XMLElement from a QString.
+ /// @param str The QString to convert from. Must be of the following syntax- "foo name=\"bar\""
+ static XMLElement fromQString(const QString &str);
+
+private:
+ /// The name of the tag.
+ QString m_name;
+ /// Attribute : value mappings.
+ AttributeToValueMap m_attrmapper;
+};
+
+#endif
diff --git a/kttsd/filters/xmltransformer/Makefile.am b/kttsd/filters/xmltransformer/Makefile.am
new file mode 100644
index 0000000..93577e5
--- /dev/null
+++ b/kttsd/filters/xmltransformer/Makefile.am
@@ -0,0 +1,24 @@
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkttsd_xmltransformerplugin.la
+
+libkttsd_xmltransformerplugin_la_SOURCES = \
+ xmltransformerconfwidget.ui \
+ xmltransformerconf.cpp \
+ xmltransformerproc.cpp \
+ xmltransformerplugin.cpp
+libkttsd_xmltransformerplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkttsd_xmltransformerplugin_la_LIBADD = $(top_builddir)/kttsd/libkttsd/libkttsd.la
+
+services_DATA = kttsd_xmltransformerplugin.desktop
+servicesdir = $(kde_servicesdir)
+
+# Install data files.
+xmltransformerdatadir = $(kde_datadir)/kttsd/xmltransformer/
+xmltransformerdata_DATA = xhtml2ssml.xsl xhtml2ssml_simple.xsl
+
+noinst_HEADERS = xmltransformerconfwidget.h
diff --git a/kttsd/filters/xmltransformer/kttsd_xmltransformerplugin.desktop b/kttsd/filters/xmltransformer/kttsd_xmltransformerplugin.desktop
new file mode 100644
index 0000000..6935088
--- /dev/null
+++ b/kttsd/filters/xmltransformer/kttsd_xmltransformerplugin.desktop
@@ -0,0 +1,88 @@
+[Desktop Entry]
+Name=XML Transformer
+Name[bg]=Трансформиране на XML
+Name[bs]=XML transformacije
+Name[ca]=Transformador XML
+Name[cs]=XML transformace
+Name[da]=XML Transformatør
+Name[de]=XML-Umformer
+Name[el]=XML μεταμορφωτής
+Name[es]=Transformador XML
+Name[et]=XML-i teisendus
+Name[fa]=تبدیل‌کنندۀ XML
+Name[fi]=XML-muunnin
+Name[fr]=Transformation XML
+Name[ga]=Trasfhoirmeoir XML
+Name[gl]=Transformador XML
+Name[hu]=XML-átalakító
+Name[is]=XML ummyndari
+Name[it]=Trasformatore XML
+Name[ja]=XML 変換
+Name[ka]=XML გარდამქმნელი
+Name[km]= កម្មវិធី​ប្លែង​ XML
+Name[mk]=XML-трансформирач
+Name[ms]=Transformer XML
+Name[nds]=XML-Ümwanneln
+Name[ne]=XML रुपान्तरणकर्ता
+Name[nl]=XML-omvormer
+Name[pa]=XML ਤਬਦੀਲੀਕਾਰ
+Name[pl]=Transformacja XML
+Name[pt]=Transformação de XML
+Name[pt_BR]=Transformador XML
+Name[ru]=Преобразователь XML
+Name[sk]=Transformácia XML
+Name[sl]=Pretvornik XML
+Name[sr]=XML трансформатор
+Name[sr@Latn]=XML transformator
+Name[sv]=XML-transformering
+Name[tg]=Тағйирдиҳии XML
+Name[tr]=XML Dönüştürücü
+Name[uk]=Перетворення XML
+Name[vi]=Trình chuyển đổi XML
+Name[zh_TW]=XML 轉換器
+Comment=Generic XML Tranformation Filter Plugin for KTTS
+Comment[bg]=Приставка филтър за трансформиране на XML за KTTS
+Comment[ca]=Connector del filtre genèric del transformador XML pel KTTS
+Comment[cs]=Modul filtru obecné XML transformace KTTS
+Comment[da]=Generisk XML Tranformationsfilter-plugin for KTTS
+Comment[de]=Generisches XML-Umformungs-Filtermodul für KTTS
+Comment[el]=Γενικό φίλτρο πρόσθετο XML μεταμόρφωσης για το KTTS
+Comment[es]=Complemento de Audio para KTTSD
+Comment[et]=KTTS-i üldine XML-i teisenduse filtri plugin
+Comment[eu]=KTTS-ren XML transformazioen iragazki-plugin generikoa
+Comment[fa]=وصلۀ پالایۀ انتقال XML عمومی برای KTTS
+Comment[fi]=Yleinen XML-muuntimen suodatin liitännäinen KTTS-ohjelmalle
+Comment[fr]=Module de filtrage de transformation XML générique pour KTTS
+Comment[gl]=Plugin de Transformación de XML para KTTS
+Comment[hu]=Általános XML-átalakító modul a KTTS-hez
+Comment[is]=Almennt XML ummynda íforrit fyrir KTTS
+Comment[it]=Plugin generico per il filtro di trasformazione XML per KTTS
+Comment[ja]=KTTS 用汎用 XML 変換フィルタプラグイン
+Comment[ka]=ზოგადი XML გარდამქმნელის ფილტრის მოდული KTTS-სთვის
+Comment[km]=កម្មវិធី​ជំនួស​តម្រង​ការ​ប្លែង​ XML ទូទៅ​សម្រាប់ KTTS
+Comment[mk]=Филтер за KTTS за трансформација на општ XML
+Comment[ms]=Plugin Penapis Transformasi XML Generik bagi KTTS
+Comment[nb]=Filter-programtillegg til KTTS for alminnelig XML-transformasjon
+Comment[nds]=KTTS-Filtermoduul för dat Ümwanneln vun XML
+Comment[ne]=KTTS का लागि जेनेरीक XML रुपान्तरण फिल्टर प्लगइन
+Comment[nl]=Generieke filterplugin voor XML-omvorming voor KTTS
+Comment[pa]=KTTS ਲਈ XML ਸੰਚਾਰ ਫਿਲਟਰ
+Comment[pl]=Wtyczka transformacji XML dla KTTS
+Comment[pt]='Plugin' Genérico de Transformação de XML para o KTTS
+Comment[pt_BR]=Plug-in de Filtro Genérico de Transformação XML: para o KTTSD
+Comment[ru]=Фильтр XML для KTTS
+Comment[sk]=Všeobecný modul filtra pre transformáciu XML v KTTS
+Comment[sl]=Filtrni vstavek KTTS za generično preoblikovanje XML
+Comment[sr]=Генерички филтерски прикључак KTTS-а за трансформацију XML-а
+Comment[sr@Latn]=Generički filterski priključak KTTS-a za transformaciju XML-a
+Comment[sv]=Insticksprogram för KTTS med generellt XML-transformeringsfilter
+Comment[ta]=KTTSக்கான ஜெனரிக் XML மாற்று அலங்கார சொருகுப்பொருள்
+Comment[tg]=Филтри XMLбарои KTTS
+Comment[tr]=KTTS için genel XML Dönüştürücü Süzgeci Eklentisi
+Comment[uk]=Втулок загального фільтра перетворення XML для KTTS
+Comment[vi]=Trình bổ sung Lọc Chuyển đổi XML Chung cho KTTS
+Comment[zh_TW]=KTTS 使用的一般 XML 轉換外掛程式
+Type=Service
+ServiceTypes=KTTSD/FilterPlugin
+X-KDE-Library=libkttsd_xmltransformerplugin
+X-KDE-Languages=en,en_US,en_GB,en_CA,es,es_mx,cy,de,fi,cs,pl
diff --git a/kttsd/filters/xmltransformer/xhtml2ssml.xsl b/kttsd/filters/xmltransformer/xhtml2ssml.xsl
new file mode 100644
index 0000000..5c7ca33
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xhtml2ssml.xsl
@@ -0,0 +1,252 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ***********************************************************************
+ Stylesheet for transforming XHTML into SSML markup.
+ ============
+ Copyright : (C) 2005 by Gary Cramblitt
+ ============
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************** -->
+
+<!-- ***********************************************************************
+ The rendering is roughly based on the stylesheet from Appendix A of the
+ CSS2 specification, http://www.w3.org/TR/REC-CSS2/sample.html
+
+ @media speech {
+ H1, H2, H3,
+ H4, H5, H6 { voice-family: paul, male; stress: 20; richness: 90 }
+ H1 { pitch: x-low; pitch-range: 90 }
+ H2 { pitch: x-low; pitch-range: 80 }
+ H3 { pitch: low; pitch-range: 70 }
+ H4 { pitch: medium; pitch-range: 60 }
+ H5 { pitch: medium; pitch-range: 50 }
+ H6 { pitch: medium; pitch-range: 40 }
+ LI, DT, DD { pitch: medium; richness: 60 }
+ DT { stress: 80 }
+ PRE, CODE, TT { pitch: medium; pitch-range: 0; stress: 0; richness: 80 }
+ EM { pitch: medium; pitch-range: 60; stress: 60; richness: 50 }
+ STRONG { pitch: medium; pitch-range: 60; stress: 90; richness: 90 }
+ DFN { pitch: high; pitch-range: 60; stress: 60 }
+ S, STRIKE { richness: 0 }
+ I { pitch: medium; pitch-range: 60; stress: 60; richness: 50 }
+ B { pitch: medium; pitch-range: 60; stress: 90; richness: 90 }
+ U { richness: 0 }
+ A:link { voice-family: harry, male }
+ A:visited { voice-family: betty, female }
+ A:active { voice-family: betty, female; pitch-range: 80; pitch: x-high }
+}
+
+As SSML does not seem to offer an equivalent for "stress" and "richness".
+They are mapped to rate and volume respectively.
+
+ H1 { male; pitch: x-low; range: x-high; rate: slow; volume: x-loud}
+ H2 { male; pitch: x-low; range: high; rate: slow; volume: x-loud }
+ H3 { male; pitch: low; range: high; rate: slow; volume: x-loud }
+ H4 { male; pitch: medium; range: medium; rate: slow; volume: x-loud }
+ H5 { male; pitch: medium; range: low; rate: slow; volume: x-loud }
+ H6 { male; pitch: medium; range: x-low; rate: slow; volume: x-loud }
+ LI, DD { pitch: medium; }
+ DT { pitch: medium; rate: x-fast }
+ PRE, CODE, TT { pitch: medium; range: x-low; rate: slow; volume: loud }
+ EM { pitch: medium; range: medium; rate: medium; volume: loud }
+ STRONG { pitch: medium; range: medium; rate: x-fast; volume: x-loud }
+ DFN { pitch: high; range: medium; rate: medium }
+ S, STRIKE { volume: x-soft }
+ I { pitch: high; range: medium; rate: fast; volume: medium }
+ B { pitch: high; range: medium; rate: x-fast; volume: x-loud }
+ U { volume: medium }
+ A { female }
+
+ *********************************************************************** -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="xml" indent="no"/>
+
+<!-- root -->
+<xsl:template match="/">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- html -->
+<!-- local-name() must be used in order to ignore namespaces. -->
+<xsl:template match="*[local-name()='html' or local-name()='HTML']">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- Ignore header, speak the body of xhtml document. -->
+<xsl:template match="*[local-name()='head' or local-name()='HEAD']"/>
+<xsl:template match="*[local-name()='body' or local-name()='BODY']">
+ <xsl:element name="speak">
+ <xsl:copy-of select="/html/@lang"/>
+ <xsl:copy-of select="/HTML/@lang"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+</xsl:template>
+
+<!-- Paragraph -->
+<xsl:template match="*[local-name()='p' or local-name()='P']">
+ <p><xsl:apply-templates/></p>
+</xsl:template>
+
+<!-- H1 { pitch: x-low; range: x-high; rate: slow; volume: x-loud } -->
+<xsl:template match="*[local-name()='h1' or local-name()='H1']">
+ <voice gender="male"><prosody pitch="x-low" range="x-high" rate="slow" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody></voice>
+</xsl:template>
+
+<!-- H2 { male; pitch: x-low; range: high; rate: slow; volume: x-loud } -->
+<xsl:template match="*[local-name()='h2' or local-name()='H2']">
+ <voice gender="male"><prosody pitch="x-low" range="high" rate="slow" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody></voice>
+</xsl:template>
+
+<!-- H3 { male; pitch: low; range: high; rate: slow; volume: x-loud } -->
+<xsl:template match="*[local-name()='h3' or local-name()='H3']">
+ <voice gender="male"><prosody pitch="low" range="high" rate="slow" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody></voice>
+</xsl:template>
+
+<!-- H4 { male; pitch: medium; range: medium; rate: slow; volume: x-loud } -->
+<xsl:template match="*[local-name()='h4' or local-name()='H4']">
+ <voice gender="male"><prosody pitch="medium" range="medium" rate="slow" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody></voice>
+</xsl:template>
+
+<!-- H5 { male; pitch: medium; range: low; rate: slow; volume: x-loud } -->
+<xsl:template match="*[local-name()='h5' or local-name()='H5']">
+ <voice gender="male"><prosody pitch="low" range="low" rate="slow" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody></voice>
+</xsl:template>
+
+<!-- H6 { male; pitch: medium; range: x-low; rate: slow; volume: x-loud } -->
+<xsl:template match="*[local-name()='h6' or local-name()='H6']">
+ <voice gender="male"><prosody pitch="medium" range="x-low" rate="slow" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody></voice>
+</xsl:template>
+
+<!-- LI, DD { pitch: medium; } -->
+<xsl:template match="*[local-name()='li' or local-name()='LI']">
+ <prosody pitch="medium">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+<xsl:template match="*[local-name()='dd' or local-name()='DD']">
+ <prosody pitch="medium">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- DT { pitch: medium; rate: x-fast } -->
+<xsl:template match="*[local-name()='dt' or local-name()='DT']">
+ <prosody pitch="medium" rate="x-fast">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- PRE, CODE, TT { pitch: medium; range: x-low; rate: slow; volume: loud } -->
+<xsl:template match="*[local-name()='pre' or local-name()='PRE']">
+ <prosody pitch="medium" range="x-low" rate="slow" volume="loud">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+<xsl:template match="*[local-name()='code' or local-name()='CODE']">
+ <prosody pitch="medium" range="x-low" rate="slow" volume="loud">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+<xsl:template match="*[local-name()='tt' or local-name()='TT']">
+ <prosody pitch="medium" range="x-low" rate="slow" volume="loud">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- EM { pitch: medium; range: medium; rate: medium; volume: loud } -->
+<xsl:template match="*[local-name()='em' or local-name()='EM']">
+ <prosody pitch="medium" range="medium" rate="medium" volume="loud">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- STRONG { pitch: medium; range: medium; rate: x-fast; volume: x-loud } -->
+<xsl:template match="*[local-name()='strong' or local-name()='STRONG']">
+ <prosody pitch="medium" range="medium" rate="x-fast" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- DFN { pitch: high; range: medium; rate: medium } -->
+<xsl:template match="*[local-name()='dfn' or local-name()='DFN']">
+ <prosody pitch="high" range="medium" rate="medium">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- S, STRIKE { volume: x-soft } -->
+<xsl:template match="*[local-name()='s' or local-name()='S']">
+ <prosody volume="x-soft">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+<xsl:template match="*[local-name()='strike' or local-name()='STRIKE']">
+ <prosody volume="x-soft">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- I { pitch: high; range: medium; rate: fast; volume: medium } -->
+<xsl:template match="*[local-name()='i' or local-name()='I']">
+ <prosody pitch="high" range="medium" rate="fast" volume="medium">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- B { pitch: high; range: medium; rate: x-fast; volume: x-loud } -->
+<xsl:template match="*[local-name()='b' or local-name()='B']">
+ <prosody pitch="high" range="medium" rate="x-fast" volume="x-loud">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- U { volume: medium } -->
+<xsl:template match="*[local-name()='u' or local-name()='U']">
+ <prosody pitch="medium">
+ <xsl:apply-templates/>
+ </prosody>
+</xsl:template>
+
+<!-- A { female } -->
+<xsl:template match="*[local-name()='a' or local-name()='A']">
+ <voice gender="female">
+ <xsl:apply-templates/>
+ </voice>
+</xsl:template>
+
+<!-- HREF attribute -->
+<xsl:template match="@href">
+ <prosody volume="soft">Address</prosody>
+ <prosody rate="x-fast">
+ <xsl:value-of select="."/>
+ </prosody>
+</xsl:template>
+
+<!-- Ignore scripts. -->
+<xsl:template match="*[local-name()='script' or local-name()='SCRIPT']"/>
+
+<!-- Ignore styles. -->
+<xsl:template match="*[local-name()='style' or local-name()='STYLE']"/>
+
+</xsl:stylesheet>
diff --git a/kttsd/filters/xmltransformer/xhtml2ssml_simple.xsl b/kttsd/filters/xmltransformer/xhtml2ssml_simple.xsl
new file mode 100644
index 0000000..2c1f3be
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xhtml2ssml_simple.xsl
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ***********************************************************************
+ Stylesheet for transforming XHTML into SSML markup.
+ ============
+ Copyright : (C) 2005 by Gary Cramblitt
+ ============
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************** -->
+
+<!-- ***********************************************************************
+ The rendering takes a minimalist approach, mapping <b>, <em>, <i>
+ etc. to louder voices. Everything else is pretty much mapped to just
+ paragraph and sentence tags. Hyperlink addresses are spoken fast.
+ *********************************************************************** -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="xml" indent="no"/>
+
+<!-- root -->
+<xsl:template match="/">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- html -->
+<!-- local-name() must be used in order to ignore namespaces. -->
+<xsl:template match="*[local-name()='html' or local-name()='HTML']">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- Ignore header, speak the body of xhtml document. -->
+<xsl:template match="*[local-name()='head' or local-name()='HEAD']"/>
+<xsl:template match="*[local-name()='body' or local-name()='BODY']">
+ <xsl:element name="speak">
+ <xsl:copy-of select="/html/@lang"/>
+ <xsl:copy-of select="/HTML/@lang"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+</xsl:template>
+
+<!-- Paragraph -->
+<xsl:template match="*[local-name()='p' or local-name()='P']">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- H1, H2, H3, H4, H5, H6: ignore tag, speak content as sentence. -->
+<xsl:template match="*[contains('h1|h2|h3|h4|h5|h6|H1|H2|H3|H4|H5|H6|',concat(local-name(),'|'))]">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- DFN, LI, DD, DT: ignore tag, speak content. -->
+<xsl:template match="*[contains('dfn|li|dd|dt|DFN|LI|DD|DT|',concat(local-name(),'|'))]">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- PRE, CODE, TT; ignore tag, speak content. -->
+<xsl:template match="*[contains('pre|code|tt|PRE|CODE|TT|',concat(local-name(),'|'))]">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- EM, STRONG, I, B, S, STRIKE, U: speak emphasized. -->
+<xsl:template match="*[contains('em|strong|i|b|s|strike|EM|STRONG|I|B|S|STRIKE|',concat(local-name(),'|'))]">
+ <emphasis level="strong">
+ <xsl:apply-templates/>
+ </emphasis>
+</xsl:template>
+
+<!-- A: speak hyperlink emphasized, address fast. -->
+<xsl:template match="*[local-name()='a' or local-name()='A']">
+ <xsl:if test="@href">
+ <emphasis level="moderate">
+ <xsl:apply-templates/>
+ </emphasis>
+ <prosody rate="fast" volume="soft">
+ <xsl:value-of select="'Link to. '"/>
+ <xsl:value-of select="@href"/>
+ </prosody>
+ </xsl:if>
+</xsl:template>
+
+<!-- Ignore scripts. -->
+<xsl:template match="*[local-name()='script' or local-name()='SCRIPT']"/>
+
+<!-- Ignore styles. -->
+<xsl:template match="*[local-name()='style' or local-name()='STYLE']"/>
+
+</xsl:stylesheet>
diff --git a/kttsd/filters/xmltransformer/xmltransformerconf.cpp b/kttsd/filters/xmltransformer/xmltransformerconf.cpp
new file mode 100644
index 0000000..76cfd70
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xmltransformerconf.cpp
@@ -0,0 +1,182 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic XML Transformation Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qstring.h>
+#include <qlayout.h>
+
+// KDE includes.
+#include <klocale.h>
+#include <klineedit.h>
+#include <kconfig.h>
+#include <kdialog.h>
+#include <kurlrequester.h>
+#include <kstandarddirs.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// XmlTransformer includes.
+#include "xmltransformerconf.h"
+#include "xmltransformerconf.moc"
+
+/**
+* Constructor
+*/
+XmlTransformerConf::XmlTransformerConf( QWidget *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterConf(parent, name)
+{
+ // kdDebug() << "XmlTransformerConf::XmlTransformerConf: Running" << endl;
+
+ // Create configuration widget.
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint(), "XmlTransformerConfWidgetLayout");
+ layout->setAlignment (Qt::AlignTop);
+ m_widget = new XmlTransformerConfWidget(this, "XmlTransformerConfigWidget");
+ layout->addWidget(m_widget);
+
+ // Set up defaults.
+ defaults();
+
+ // Connect signals.
+ connect( m_widget->nameLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+ connect( m_widget->xsltPath, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->xsltprocPath, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->rootElementLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->doctypeLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->appIdLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+}
+
+/**
+* Destructor.
+*/
+XmlTransformerConf::~XmlTransformerConf(){
+ // kdDebug() << "XmlTransformerConf::~XmlTransformerConf: Running" << endl;
+}
+
+/**
+* This method is invoked whenever the module should read its
+* configuration (most of the times from a config file) and update the
+* user interface. This happens when the user clicks the "Reset" button in
+* the control center, to undo all of his changes and restore the currently
+* valid settings. Note that kttsmgr calls this when the plugin is
+* loaded, so it not necessary to call it in your constructor.
+* The plugin should read its configuration from the specified group
+* in the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* loading your configuration.
+*/
+void XmlTransformerConf::load(KConfig* config, const QString& configGroup){
+ // kdDebug() << "XmlTransformerConf::load: Running" << endl;
+ config->setGroup( configGroup );
+ m_widget->nameLineEdit->setText( config->readEntry( "UserFilterName", m_widget->nameLineEdit->text() ) );
+ m_widget->xsltPath->setURL( config->readEntry( "XsltFilePath", m_widget->xsltPath->url() ) );
+ m_widget->xsltprocPath->setURL( config->readEntry( "XsltprocPath", m_widget->xsltprocPath->url() ) );
+ m_widget->rootElementLineEdit->setText(
+ config->readEntry( "RootElement", m_widget->rootElementLineEdit->text() ) );
+ m_widget->doctypeLineEdit->setText(
+ config->readEntry( "DocType", m_widget->doctypeLineEdit->text() ) );
+ m_widget->appIdLineEdit->setText(
+ config->readEntry( "AppID", m_widget->appIdLineEdit->text() ) );
+}
+
+/**
+* This function gets called when the user wants to save the settings in
+* the user interface, updating the config files or wherever the
+* configuration is stored. The method is called when the user clicks "Apply"
+* or "Ok". The plugin should save its configuration in the specified
+* group of the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* saving your configuration.
+*/
+void XmlTransformerConf::save(KConfig* config, const QString& configGroup){
+ // kdDebug() << "XmlTransformerConf::save: Running" << endl;
+ config->setGroup( configGroup );
+ config->writeEntry( "UserFilterName", m_widget->nameLineEdit->text() );
+ config->writeEntry( "XsltFilePath", realFilePath( m_widget->xsltPath->url() ) );
+ config->writeEntry( "XsltprocPath", realFilePath( m_widget->xsltprocPath->url() ) );
+ config->writeEntry( "RootElement", m_widget->rootElementLineEdit->text() );
+ config->writeEntry( "DocType", m_widget->doctypeLineEdit->text() );
+ config->writeEntry( "AppID", m_widget->appIdLineEdit->text().replace(" ", "") );
+}
+
+/**
+* This function is called to set the settings in the module to sensible
+* default values. It gets called when hitting the "Default" button. The
+* default values should probably be the same as the ones the application
+* uses when started without a config file. Note that defaults should
+* be applied to the on-screen widgets; not to the config file.
+*/
+void XmlTransformerConf::defaults(){
+ // kdDebug() << "XmlTransformerConf::defaults: Running" << endl;
+ // Default name.
+ m_widget->nameLineEdit->setText(i18n( "XML Transformer" ));
+ // Default XSLT path to installed xsl files.
+ m_widget->xsltPath->setURL( locate("data", "kttsd/xmltransformer/") );
+ // Default path to xsltproc.
+ m_widget->xsltprocPath->setURL( "xsltproc" );
+ // Default root element to "html".
+ m_widget->rootElementLineEdit->setText( "html" );
+ // Default doctype to blank.
+ m_widget->doctypeLineEdit->setText( "" );
+ // Default App ID to blank.
+ m_widget->appIdLineEdit->setText( "" );
+ // kdDebug() << "XmlTransformerConf::defaults: Exiting" << endl;
+}
+
+/**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+bool XmlTransformerConf::supportsMultiInstance() { return true; }
+
+/**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+QString XmlTransformerConf::userPlugInName()
+{
+ QString filePath = realFilePath(m_widget->xsltprocPath->url());
+ if (filePath.isEmpty()) return QString::null;
+ if (getLocation(filePath).isEmpty()) return QString::null;
+
+ filePath = realFilePath(m_widget->xsltPath->url());
+ if (filePath.isEmpty()) return QString::null;
+ if (getLocation(filePath).isEmpty()) return QString::null;
+ if (!QFileInfo(filePath).isFile()) return QString::null;
+
+ return m_widget->nameLineEdit->text();
+}
diff --git a/kttsd/filters/xmltransformer/xmltransformerconf.h b/kttsd/filters/xmltransformer/xmltransformerconf.h
new file mode 100644
index 0000000..86df0e2
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xmltransformerconf.h
@@ -0,0 +1,120 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic XML Transformation Filter Configuration class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _XMLTRANSFORMERCONF_H_
+#define _XMLTRANSFORMERCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// XmlTransformer includes.
+#include "xmltransformerconfwidget.h"
+
+class XmlTransformerConf : public KttsFilterConf
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ XmlTransformerConf( QWidget *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor
+ */
+ virtual ~XmlTransformerConf();
+
+ /**
+ * This method is invoked whenever the module should read its
+ * configuration (most of the times from a config file) and update the
+ * user interface. This happens when the user clicks the "Reset" button in
+ * the control center, to undo all of his changes and restore the currently
+ * valid settings. Note that KTTSMGR calls this when the plugin is
+ * loaded, so it not necessary to call it in your constructor.
+ * The plugin should read its configuration from the specified group
+ * in the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * loading your configuration.
+ *
+ * When a plugin is first added to KTTSMGR, @e load will be called with
+ * a Null @e configGroup. In this case, the plugin will not have
+ * any instance-specific parameters to load, but it may still wish
+ * to load parameters that apply to all instances of the plugin.
+ */
+ virtual void load(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function gets called when the user wants to save the settings in
+ * the user interface, updating the config files or wherever the
+ * configuration is stored. The method is called when the user clicks "Apply"
+ * or "Ok". The plugin should save its configuration in the specified
+ * group of the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * saving your configuration.
+ */
+ virtual void save(KConfig *config, const QString &configGroup);
+
+ /**
+ * This function is called to set the settings in the module to sensible
+ * default values. It gets called when hitting the "Default" button. The
+ * default values should probably be the same as the ones the application
+ * uses when started without a config file. Note that defaults should
+ * be applied to the on-screen widgets; not to the config file.
+ */
+ virtual void defaults();
+
+ /**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+ virtual bool supportsMultiInstance();
+
+ /**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+ virtual QString userPlugInName();
+
+ private slots:
+
+ private:
+ // Configuration Widget.
+ XmlTransformerConfWidget* m_widget;
+};
+
+#endif //_XMLTRANSFORMERCONF_H_
diff --git a/kttsd/filters/xmltransformer/xmltransformerconfwidget.ui b/kttsd/filters/xmltransformer/xmltransformerconfwidget.ui
new file mode 100644
index 0000000..4975d9c
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xmltransformerconfwidget.ui
@@ -0,0 +1,249 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>XmlTransformerConfWidget</class>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>XmlTransformerConfWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>548</width>
+ <height>256</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Configure XML Transformer</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter any descriptive name you like for this filter.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>xsltPath</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the full path to an XML Style Language - Transforms (XSLT) stylesheet file. XSLT files usually end with extension .xsl.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>xsltprocPath</cstring>
+ </property>
+ <property name="url" stdset="0">
+ <string>xsltproc</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the path to the xsltproc executable program. If it is in the PATH environment variable, just enter "xsltproc".</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter any descriptive name you like for this filter.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>xsltLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;XSLT file:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>xsltPath</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the full path to an XML Style Language - Transforms (XSLT) stylesheet file. XSLT files usually end with extension .xsl.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>xsltprocLabel</cstring>
+ </property>
+ <property name="text">
+ <string>xsltproc &amp;executable:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>xsltprocPath</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the path to the xsltproc executable program. If it is in the PATH environment variable, just enter "xsltproc".</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>applyGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Apply This &amp;Filter When</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These settings determines when the filter is applied to text.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>rootElementLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter will be applied only to text having the specified XML root element. If blank, applies to all text. You may enter more than one root element separated by commas. Example: "html".</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>doctypeLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter will be applied only to text having the specified DOCTYPE specification. If blank, applies to all text. You may enter more than one DOCTYPE separated by commas. Example: "xhtml".</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Enter a DCOP Application ID. This filter will only apply to text queued by that application. You may enter more than one ID separated by commas. Use &lt;b&gt;knotify&lt;/b&gt; to match all messages sent as KDE notifications. If blank, this filter applies to text queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>rootElementLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Root element is:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rootElementLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter will be applied only to text having the specified XML root element. If blank, applies to all text. You may enter more than one root element separated by commas. Example: "html".</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>doctypeLabel</cstring>
+ </property>
+ <property name="text">
+ <string>or DOC&amp;TYPE is:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>doctypeLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter will be applied only to text having the specified DOCTYPE specification. If blank, applies to all text. You may enter more than one DOCTYPE separated by commas. Example: "xhtml".</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>appIdLabel</cstring>
+ </property>
+ <property name="text">
+ <string>and Application &amp;ID contains:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Enter a DCOP Application ID. This filter will only apply to text queued by that application. You may enter more than one ID separated by commas. Use &lt;b&gt;knotify&lt;/b&gt; to match all messages sent as KDE notifications. If blank, this filter applies to text queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/filters/xmltransformer/xmltransformerplugin.cpp b/kttsd/filters/xmltransformer/xmltransformerplugin.cpp
new file mode 100644
index 0000000..c76c889
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xmltransformerplugin.cpp
@@ -0,0 +1,31 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generating the factories so XML Transformer Filter can be used as plug in.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#include <kgenericfactory.h>
+
+#include "xmltransformerconf.h"
+#include "xmltransformerproc.h"
+
+typedef K_TYPELIST_2( XmlTransformerProc, XmlTransformerConf ) XmlTransformerPlugin;
+K_EXPORT_COMPONENT_FACTORY( libkttsd_xmltransformerplugin,
+ KGenericFactory<XmlTransformerPlugin>("kttsd_xmltransformer") )
diff --git a/kttsd/filters/xmltransformer/xmltransformerproc.cpp b/kttsd/filters/xmltransformer/xmltransformerproc.cpp
new file mode 100644
index 0000000..d4aa1c5
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xmltransformerproc.cpp
@@ -0,0 +1,385 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic XML Transformation Filter Processing class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qfile.h>
+#include <qregexp.h>
+
+// KDE includes.
+#include <kdeversion.h>
+#include <kconfig.h>
+#include <ktempfile.h>
+#include <kstandarddirs.h>
+#include <kprocess.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "filterproc.h"
+#include "utils.h"
+
+// XmlTransformer includes.
+#include "xmltransformerproc.h"
+#include "xmltransformerproc.moc"
+
+/**
+ * Constructor.
+ */
+XmlTransformerProc::XmlTransformerProc( QObject *parent, const char *name, const QStringList& ) :
+ KttsFilterProc(parent, name)
+{
+ m_xsltProc = 0;
+}
+
+/**
+ * Destructor.
+ */
+/*virtual*/ XmlTransformerProc::~XmlTransformerProc()
+{
+ delete m_xsltProc;
+ if (!m_inFilename.isEmpty()) QFile::remove(m_inFilename);
+ if (!m_outFilename.isEmpty()) QFile::remove(m_outFilename);
+}
+
+/**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+bool XmlTransformerProc::init(KConfig* config, const QString& configGroup)
+{
+ // kdDebug() << "XmlTransformerProc::init: Running." << endl;
+ config->setGroup( configGroup );
+ m_UserFilterName = config->readEntry( "UserFilterName" );
+ m_xsltFilePath = config->readEntry( "XsltFilePath" );
+ m_xsltprocPath = config->readEntry( "XsltprocPath" );
+ m_rootElementList = config->readListEntry( "RootElement", ',' );
+ m_doctypeList = config->readListEntry( "DocType", ',' );
+ m_appIdList = config->readListEntry( "AppID", ',' );
+ kdDebug() << "XmlTransformerProc::init: m_xsltprocPath = " << m_xsltprocPath << endl;
+ kdDebug() << "XmlTransformerProc::init: m_xsltFilePath = " << m_xsltFilePath << endl;
+ return ( m_xsltFilePath.isEmpty() || m_xsltprocPath.isEmpty() );
+}
+
+/**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+/*virtual*/ bool XmlTransformerProc::supportsAsync() { return true; }
+
+/**
+ * Convert input, returning output.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+/*virtual*/ QString XmlTransformerProc::convert(const QString& inputText, TalkerCode* talkerCode,
+ const QCString& appId)
+{
+ // kdDebug() << "XmlTransformerProc::convert: Running." << endl;
+ // If not properly configured, do nothing.
+ if ( m_xsltFilePath.isEmpty() || m_xsltprocPath.isEmpty() )
+ {
+ kdDebug() << "XmlTransformerProc::convert: not properly configured" << endl;
+ return inputText;
+ }
+ // Asynchronously convert and wait for completion.
+ if (asyncConvert(inputText, talkerCode, appId))
+ {
+ waitForFinished();
+ m_state = fsIdle;
+ return m_text;
+ } else
+ return inputText;
+}
+
+/**
+ * Convert input. Runs asynchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ * @return False if the filter cannot perform the conversion.
+ *
+ * When conversion is completed, emits signal @ref filteringFinished. Calling
+ * program may then call @ref getOutput to retrieve converted text. Calling
+ * program must call @ref ackFinished to acknowledge the conversion.
+ */
+/*virtual*/ bool XmlTransformerProc::asyncConvert(const QString& inputText, TalkerCode* /*talkerCode*/,
+ const QCString& appId)
+{
+ m_wasModified = false;
+
+ // kdDebug() << "XmlTransformerProc::asyncConvert: Running." << endl;
+ m_text = inputText;
+ // If not properly configured, do nothing.
+ if ( m_xsltFilePath.isEmpty() || m_xsltprocPath.isEmpty() )
+ {
+ kdDebug() << "XmlTransformerProc::asyncConvert: not properly configured." << endl;
+ return false;
+ }
+
+ bool found = false;
+ // If not correct XML type, or DOCTYPE, do nothing.
+ if ( !m_rootElementList.isEmpty() )
+ {
+ // kdDebug() << "XmlTransformerProc::asyncConvert:: searching for root elements " << m_rootElementList << endl;
+ for ( uint ndx=0; ndx < m_rootElementList.count(); ++ndx )
+ {
+ if ( KttsUtils::hasRootElement( inputText, m_rootElementList[ndx] ) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found && m_doctypeList.isEmpty() )
+ {
+ kdDebug() << "XmlTransformerProc::asyncConvert: Did not find root element(s)" << m_rootElementList << endl;
+ return false;
+ }
+ }
+ if ( !found && !m_doctypeList.isEmpty() )
+ {
+ for ( uint ndx=0; ndx < m_doctypeList.count(); ++ndx )
+ {
+ if ( KttsUtils::hasDoctype( inputText, m_doctypeList[ndx] ) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ {
+ // kdDebug() << "XmlTransformerProc::asyncConvert: Did not find doctype(s)" << m_doctypeList << endl;
+ return false;
+ }
+ }
+
+ // If appId doesn't match, return input unmolested.
+ if ( !m_appIdList.isEmpty() )
+ {
+ QString appIdStr = appId;
+ // kdDebug() << "XmlTransformrProc::convert: converting " << inputText << " if appId "
+ // << appId << " matches " << m_appIdList << endl;
+ found = false;
+ for ( uint ndx=0; ndx < m_appIdList.count(); ++ndx )
+ {
+ if ( appIdStr.contains(m_appIdList[ndx]) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ {
+ // kdDebug() << "XmlTransformerProc::asyncConvert: Did not find appId(s)" << m_appIdList << endl;
+ return false;
+ }
+ }
+
+ /// Write @param text to a temporary file.
+ KTempFile inFile(locateLocal("tmp", "kttsd-"), ".xml");
+ m_inFilename = inFile.file()->name();
+ QTextStream* wstream = inFile.textStream();
+ if (wstream == 0) {
+ /// wtf...
+ kdDebug() << "XmlTransformerProc::convert: Can't write to " << m_inFilename << endl;;
+ return false;
+ }
+ // TODO: Is encoding an issue here?
+ // If input does not have xml processing instruction, add it.
+ if (!inputText.startsWith("<?xml")) *wstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ // FIXME: Temporary Fix until Konqi returns properly formatted xhtml with & coded as &amp;
+ // This will change & inside a CDATA section, which is not good, and also within comments and
+ // processing instructions, which is OK because we don't speak those anyway.
+ QString text = inputText;
+ text.replace(QRegExp("&(?!amp;)"),"&amp;");
+ *wstream << text;
+ inFile.close();
+#if KDE_VERSION >= KDE_MAKE_VERSION (3,3,0)
+ inFile.sync();
+#endif
+
+ // Get a temporary output file name.
+ KTempFile outFile(locateLocal("tmp", "kttsd-"), ".output");
+ m_outFilename = outFile.file()->name();
+ outFile.close();
+ // outFile.unlink(); // only activate this if necessary.
+
+ /// Spawn an xsltproc process to apply our stylesheet to input file.
+ m_xsltProc = new KProcess;
+ *m_xsltProc << m_xsltprocPath;
+ *m_xsltProc << "-o" << m_outFilename << "--novalid"
+ << m_xsltFilePath << m_inFilename;
+ // Warning: This won't compile under KDE 3.2. See FreeTTS::argsToStringList().
+ // kdDebug() << "SSMLConvert::transform: executing command: " <<
+ // m_xsltProc->args() << endl;
+
+ m_state = fsFiltering;
+ connect(m_xsltProc, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotProcessExited(KProcess*)));
+ connect(m_xsltProc, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(slotReceivedStdout(KProcess*, char*, int)));
+ connect(m_xsltProc, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(slotReceivedStderr(KProcess*, char*, int)));
+ if (!m_xsltProc->start(KProcess::NotifyOnExit,
+ static_cast<KProcess::Communication>(KProcess::Stdout | KProcess::Stderr)))
+ {
+ kdDebug() << "XmlTransformerProc::convert: Error starting xsltproc" << endl;
+ m_state = fsIdle;
+ return false;
+ }
+ return true;
+}
+
+// Process output when xsltproc exits.
+void XmlTransformerProc::processOutput()
+{
+ QFile::remove(m_inFilename);
+
+ int exitStatus = 11;
+ if (m_xsltProc->normalExit())
+ exitStatus = m_xsltProc->exitStatus();
+ else
+ kdDebug() << "XmlTransformerProc::processOutput: xsltproc was killed." << endl;
+
+ delete m_xsltProc;
+ m_xsltProc = 0;
+
+ if (exitStatus != 0)
+ {
+ kdDebug() << "XmlTransformerProc::processOutput: xsltproc abnormal exit. Status = " << exitStatus << endl;
+ m_state = fsFinished;
+ QFile::remove(m_outFilename);
+ emit filteringFinished();
+ return;
+ }
+
+ /// Read back the data that was written to /tmp/fileName.output.
+ QFile readfile(m_outFilename);
+ if(!readfile.open(IO_ReadOnly)) {
+ /// uhh yeah... Issues writing to the output file.
+ kdDebug() << "XmlTransformerProc::processOutput: Could not read file " << m_outFilename << endl;
+ m_state = fsFinished;
+ emit filteringFinished();
+ }
+ QTextStream rstream(&readfile);
+ m_text = rstream.read();
+ readfile.close();
+
+ kdDebug() << "XmlTransformerProc::processOutput: Read file at " + m_inFilename + " and created " + m_outFilename + " based on the stylesheet at " << m_xsltFilePath << endl;
+
+ // Clean up.
+ QFile::remove(m_outFilename);
+
+ m_state = fsFinished;
+ m_wasModified = true;
+ emit filteringFinished();
+}
+
+/**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+/*virtual*/ void XmlTransformerProc::waitForFinished()
+{
+ if (m_xsltProc)
+ {
+ if (m_xsltProc->isRunning())
+ {
+ if ( !m_xsltProc->wait( 15 ) )
+ {
+ m_xsltProc->kill();
+ kdDebug() << "XmlTransformerProc::waitForFinished: After waiting 15 seconds, xsltproc process seems to hung. Killing it." << endl;
+ processOutput();
+ }
+ }
+ }
+}
+
+/**
+ * Returns the state of the Filter.
+ */
+/*virtual*/ int XmlTransformerProc::getState() { return m_state; }
+
+/**
+ * Returns the filtered output.
+ */
+/*virtual*/ QString XmlTransformerProc::getOutput() { return m_text; }
+
+/**
+ * Acknowledges the finished filtering.
+ */
+/*virtual*/ void XmlTransformerProc::ackFinished()
+{
+ m_state = fsIdle;
+ m_text = QString::null;
+}
+
+/**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+/*virtual*/ void XmlTransformerProc::stopFiltering()
+{
+ m_state = fsStopping;
+ m_xsltProc->kill();
+}
+
+/**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+/*virtual*/ bool XmlTransformerProc::wasModified() { return m_wasModified; }
+
+void XmlTransformerProc::slotProcessExited(KProcess*)
+{
+ // kdDebug() << "XmlTransformerProc::slotProcessExited: xsltproc has exited." << endl;
+ processOutput();
+}
+
+void XmlTransformerProc::slotReceivedStdout(KProcess*, char* /*buffer*/, int /*buflen*/)
+{
+ // QString buf = QString::fromLatin1(buffer, buflen);
+ // kdDebug() << "XmlTransformerProc::slotReceivedStdout: Received from xsltproc: " << buf << endl;
+}
+
+void XmlTransformerProc::slotReceivedStderr(KProcess*, char* buffer, int buflen)
+{
+ QString buf = QString::fromLatin1(buffer, buflen);
+ kdDebug() << "XmlTransformerProc::slotReceivedStderr: Received error from xsltproc: " << buf << endl;
+}
+
diff --git a/kttsd/filters/xmltransformer/xmltransformerproc.h b/kttsd/filters/xmltransformer/xmltransformerproc.h
new file mode 100644
index 0000000..7c88dd1
--- /dev/null
+++ b/kttsd/filters/xmltransformer/xmltransformerproc.h
@@ -0,0 +1,167 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generic XML Transformation Filter Processing class.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#ifndef _XMLTRANSFORMERPROC_H_
+#define _XMLTRANSFORMERPROC_H_
+
+// Qt includes.
+#include <qobject.h>
+#include <qstringlist.h>
+
+// KTTS includes.
+#include "filterproc.h"
+
+class KProcess;
+
+class XmlTransformerProc : virtual public KttsFilterProc
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor.
+ */
+ XmlTransformerProc( QObject *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor.
+ */
+ virtual ~XmlTransformerProc();
+
+ /**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+ virtual bool init(KConfig *config, const QString &configGroup);
+
+ /**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+ virtual bool supportsAsync();
+
+ /**
+ * Convert input, returning output.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+ virtual QString convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId);
+
+ /**
+ * Convert input. Runs asynchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ * @return False if the filter cannot perform the conversion.
+ *
+ * When conversion is completed, emits signal @ref filteringFinished. Calling
+ * program may then call @ref getOutput to retrieve converted text. Calling
+ * program must call @ref ackFinished to acknowledge the conversion.
+ */
+ virtual bool asyncConvert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId);
+
+ /**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+ virtual void waitForFinished();
+
+ /**
+ * Returns the state of the Filter.
+ */
+ virtual int getState();
+
+ /**
+ * Returns the filtered output.
+ */
+ virtual QString getOutput();
+
+ /**
+ * Acknowledges the finished filtering.
+ */
+ virtual void ackFinished();
+
+ /**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+ virtual void stopFiltering();
+
+ /**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+ virtual bool wasModified();
+
+private slots:
+ void slotProcessExited(KProcess*);
+ void slotReceivedStdout(KProcess* proc, char* buffer, int buflen);
+ void slotReceivedStderr(KProcess* proc, char* buffer, int buflen);
+
+private:
+ // Process output when xsltproc exits.
+ void processOutput();
+
+ // If not empty, only apply to text queued by an applications containing one of these strings.
+ QStringList m_appIdList;
+ // If not empty, only apply to XML that has the specified root element.
+ QStringList m_rootElementList;
+ // If not empty, only apply to XML that has the specified DOCTYPE spec.
+ QStringList m_doctypeList;
+ // The text that is being filtered.
+ QString m_text;
+ // Processing state.
+ int m_state;
+ // xsltproc process.
+ KProcess* m_xsltProc;
+ // Input and Output filenames.
+ QString m_inFilename;
+ QString m_outFilename;
+ // User's name for the filter.
+ QString m_UserFilterName;
+ // XSLT file.
+ QString m_xsltFilePath;
+ // Path to xsltproc processor.
+ QString m_xsltprocPath;
+ // Did this filter modify the text?
+ bool m_wasModified;
+};
+
+#endif // _XMLTRANSFORMERPROC_H_