1
/***************************************************** vim:set ts=4 sw=4 sts=4:
2
KControl module for KTTSD configuration and Job Management
4
Copyright 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
5
Copyright 2004-2005 by Gary Cramblitt <garycramblitt@comcast.net>
6
Copyright 2009 by Jeremy Whiting <jpwhiting@kde.org>
8
Original author: José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
9
Current Maintainer: Jeremy Whiting <jpwhiting@kde.org>
11
This program is free software; you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation; either version 2 of the License, or
14
(at your option) any later version.
16
This program is distributed in the hope that it will be useful,
17
but WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
GNU General Public License for more details.
21
You should have received a copy of the GNU General Public License
22
along with this program; if not, write to the Free Software
23
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24
***************************************************************************/
26
// Note to programmers. There is a subtle difference between a plugIn name and a
27
// synthesizer name. The latter is a translated name, for example, "Festival Interactivo",
28
// while the former is alway an English name, example "Festival Interactive".
30
// KCMKttsMgr includes.
31
#include "kcmkttsmgr.h"
32
#include "kcmkttsmgr.moc"
38
#include <QtGui/QWidget>
39
#include <QtGui/QTabWidget>
40
#include <QtGui/QCheckBox>
41
#include <QtGui/QLayout>
42
#include <QtGui/QRadioButton>
43
#include <QtGui/QSlider>
44
#include <QtGui/QLabel>
45
#include <QtGui/QTreeWidget>
46
#include <QtGui/QHeaderView>
47
#include <QtCore/QTextStream>
48
#include <QtGui/QMenu>
51
#include <kparts/componentfactory.h>
52
#include <klineedit.h>
53
#include <kurlrequester.h>
55
#include <kstandarddirs.h>
56
#include <kaboutdata.h>
58
#include <knuminput.h>
59
#include <kcombobox.h>
60
#include <kinputdialog.h>
61
#include <kmessagebox.h>
62
#include <kfiledialog.h>
63
#include <ktoolinvocation.h>
66
#include <kpluginfactory.h>
67
#include <kpluginloader.h>
70
#include "talkercode.h"
71
#include "filterconf.h"
72
#include "selecttalkerdlg.h"
73
#include "selectlanguagedlg.h"
75
#include "kttsjobmgr.h"
78
// Defaults set when clicking Defaults button.
79
const bool autostartMgrCheckBoxValue = true;
80
const bool autoexitMgrCheckBoxValue = true;
83
// Make this a plug in.
84
K_PLUGIN_FACTORY (KCMKttsMgrFactory, registerPlugin<KCMKttsMgr>();)
85
K_EXPORT_PLUGIN (KCMKttsMgrFactory ("jovie"))
88
// ----------------------------------------------------------------------------
90
FilterListModel::FilterListModel (FilterList filters, QObject *parent)
91
: QAbstractListModel (parent), m_filters (filters)
95
int FilterListModel::rowCount (const QModelIndex &parent) const
97
if (!parent.isValid())
98
return m_filters.count();
103
int FilterListModel::columnCount (const QModelIndex &parent) const
109
QModelIndex FilterListModel::index (int row, int column, const QModelIndex &parent) const
111
if (!parent.isValid())
112
return createIndex (row, column, 0);
114
return QModelIndex();
117
QModelIndex FilterListModel::parent (const QModelIndex &index) const
120
return QModelIndex();
123
QVariant FilterListModel::data (const QModelIndex &index, int role) const
125
if (!index.isValid())
128
if (index.row() < 0 || index.row() >= m_filters.count())
131
if (index.column() < 0 || index.column() >= 2)
134
if (role == Qt::DisplayRole || role == Qt::EditRole)
135
switch (index.column()) {
140
return m_filters.at (index.row()).userFilterName;
144
if (role == Qt::CheckStateRole)
145
switch (index.column()) {
147
if (m_filters.at (index.row()).enabled)
150
return Qt::Unchecked;
160
Qt::ItemFlags FilterListModel::flags (const QModelIndex &index) const
162
if (!index.isValid())
163
return Qt::ItemIsEnabled;
165
switch (index.column()) {
167
return QAbstractItemModel::flags (index) | Qt::ItemIsEnabled |
168
Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
171
return QAbstractItemModel::flags (index) | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
174
return QAbstractItemModel::flags (index) | Qt::ItemIsEnabled;
177
QVariant FilterListModel::headerData (int section, Qt::Orientation orientation, int role) const
179
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
182
return QLatin1String( "" );
184
return i18n ("Filter");
189
bool FilterListModel::removeRow (int row, const QModelIndex & parent)
191
beginRemoveRows (parent, row, row);
192
m_filters.removeAt (row);
197
FilterItem FilterListModel::getRow (int row) const
199
if (row < 0 || row >= rowCount()) return FilterItem();
200
return m_filters[row];
203
bool FilterListModel::appendRow (FilterItem& filter)
205
beginInsertRows (QModelIndex(), m_filters.count(), m_filters.count());
206
m_filters.append (filter);
211
bool FilterListModel::updateRow (int row, FilterItem& filter)
213
m_filters.replace (row, filter);
214
emit dataChanged (index (row, 0, QModelIndex()), index (row, columnCount() - 1, QModelIndex()));
218
bool FilterListModel::swap (int i, int j)
220
m_filters.swap (i, j);
221
emit dataChanged (index (i, 0, QModelIndex()), index (j, columnCount() - 1, QModelIndex()));
225
void FilterListModel::clear()
231
// ----------------------------------------------------------------------------
236
KCMKttsMgr::KCMKttsMgr (QWidget *parent, const QVariantList &) :
237
KCModule (KCMKttsMgrFactory::componentData(), parent/*, name*/),
241
// kDebug() << "KCMKttsMgr constructor running.";
243
// Initialize some variables.
248
m_suppressConfigChanged = false;
250
// Add the KTTS Manager widget
253
// Connect Views to Models and set row selection mode.
254
talkersView->setModel (&m_talkerListModel);
255
filtersView->setModel (&m_filterListModel);
256
talkersView->setSelectionBehavior (QAbstractItemView::SelectRows);
257
filtersView->setSelectionBehavior (QAbstractItemView::SelectRows);
258
talkersView->setRootIsDecorated (false);
259
filtersView->setRootIsDecorated (false);
260
talkersView->setItemsExpandable (false);
261
filtersView->setItemsExpandable (false);
263
// Give buttons icons.
265
higherTalkerPriorityButton->setIcon (KIcon ( QLatin1String( "go-up" )));
266
lowerTalkerPriorityButton->setIcon (KIcon ( QLatin1String( "go-down" )));
267
removeTalkerButton->setIcon (KIcon ( QLatin1String( "user-trash" )));
268
configureTalkerButton->setIcon (KIcon ( QLatin1String( "configure" )));
271
higherFilterPriorityButton->setIcon (KIcon ( QLatin1String( "go-up" )));
272
lowerFilterPriorityButton->setIcon (KIcon ( QLatin1String( "go-down" )));
273
removeFilterButton->setIcon (KIcon ( QLatin1String( "user-trash" )));
274
configureFilterButton->setIcon (KIcon ( QLatin1String( "configure" )));
276
// Object for the KTTSD configuration.
277
m_config = new KConfig (QLatin1String( "kttsdrc" ));
279
// Connect the signals from the KCMKtssMgrWidget to this class.
282
connect (enableJovieCheckBox, SIGNAL (toggled(bool)),
283
SLOT (slotEnableJovie_toggled(bool)));
286
connect (addTalkerButton, SIGNAL (clicked()),
287
this, SLOT (slotAddTalkerButton_clicked()));
288
connect (higherTalkerPriorityButton, SIGNAL (clicked()),
289
this, SLOT (slotHigherTalkerPriorityButton_clicked()));
290
connect (lowerTalkerPriorityButton, SIGNAL (clicked()),
291
this, SLOT (slotLowerTalkerPriorityButton_clicked()));
292
connect (removeTalkerButton, SIGNAL (clicked()),
293
this, SLOT (slotRemoveTalkerButton_clicked()));
294
connect (configureTalkerButton, SIGNAL (clicked()),
295
this, SLOT (slotConfigureTalkerButton_clicked()));
296
connect (talkersView, SIGNAL (clicked(QModelIndex)),
297
this, SLOT (updateTalkerButtons()));
300
connect (addFilterButton, SIGNAL (clicked()),
301
this, SLOT (slotAddFilterButton_clicked()));
302
connect (higherFilterPriorityButton, SIGNAL (clicked()),
303
this, SLOT (slotHigherFilterPriorityButton_clicked()));
304
connect (lowerFilterPriorityButton, SIGNAL (clicked()),
305
this, SLOT (slotLowerFilterPriorityButton_clicked()));
306
connect (removeFilterButton, SIGNAL (clicked()),
307
this, SLOT (slotRemoveFilterButton_clicked()));
308
connect (configureFilterButton, SIGNAL (clicked()),
309
this, SLOT (slotConfigureFilterButton_clicked()));
310
connect (filtersView, SIGNAL (clicked(QModelIndex)),
311
this, SLOT (updateFilterButtons()));
312
connect (filtersView, SIGNAL (clicked(QModelIndex)),
313
this, SLOT (slotFilterListView_clicked(QModelIndex)));
317
connect (mainTab, SIGNAL (currentChanged(int)),
318
this, SLOT (slotTabChanged()));
320
// See if Jovie is already running, and if so, create jobs tab.
321
if (QDBusConnection::sessionBus().interface()->isServiceRegistered (QLatin1String( "org.kde.jovie" )))
324
// Start Jovie if check box is checked.
325
slotEnableJovie_toggled (enableJovieCheckBox->isChecked());
327
// Adjust view column sizes.
328
// TODO: To work properly, this needs to be done after the widgets are shown.
329
// Possibly in Resize event?
330
for (int i = 0; i < m_filterListModel.columnCount(); ++i)
331
filtersView->resizeColumnToContents (i);
332
for (int i = 0; i < m_talkerListModel.columnCount(); ++i)
333
talkersView->resizeColumnToContents (i);
335
// Switch to Talkers tab if none configured,
336
// otherwise switch to Jobs tab if it is active.
337
if (m_talkerListModel.rowCount() == 0)
338
mainTab->setCurrentIndex (wpTalkers);
339
else if (enableJovieCheckBox->isChecked())
340
mainTab->setCurrentIndex (wpJobs);
346
KCMKttsMgr::~KCMKttsMgr()
348
// kDebug() << "KCMKttsMgr::~KCMKttsMgr: Running";
353
* This method is invoked whenever the module should read its
354
* configuration (most of the times from a config file) and update the
355
* user interface. This happens when the user clicks the "Reset" button in
356
* the control center, to undo all of his changes and restore the currently
357
* valid settings. NOTE that this is not called after the modules is loaded,
358
* so you probably want to call this method in the constructor.
360
void KCMKttsMgr::load()
362
// kDebug() << "KCMKttsMgr::load: Running";
365
// Don't emit changed() signal while loading.
366
m_suppressConfigChanged = true;
368
// Set the group general for the configuration of kttsd itself (no plug ins)
369
KConfigGroup generalConfig (m_config, "General");
372
enableJovieCheckBox->setChecked (generalConfig.readEntry ("EnableJovie",
373
enableJovieCheckBox->isChecked()));
375
// Last filter ID. Used to generate a new ID for an added filter.
378
// Load existing Talkers into Talker List.
379
m_talkerListModel.loadTalkerCodesFromConfig (m_config);
381
// Last talker ID. Used to generate a new ID for an added talker.
382
//m_lastTalkerID = m_talkerListModel.highestTalkerId();
384
// Dictionary mapping languages to language codes.
385
m_languagesToCodes.clear();
386
for (int i = 0; i < m_talkerListModel.rowCount(); ++i) {
387
QString language = m_talkerListModel.getRow (i).language();
388
m_languagesToCodes[language] = language;
391
// Iterate thru the possible plug ins getting their language support codes.
392
//for(int i=0; i < offers.count() ; ++i)
394
// QString synthName = offers[i]->name();
395
// QStringList languageCodes = offers[i]->property("X-KDE-Languages").toStringList();
396
// // Add language codes to the language-to-language code map.
397
// QStringList::ConstIterator endLanguages(languageCodes.constEnd());
398
// for( QStringList::ConstIterator it = languageCodes.constBegin(); it != endLanguages; ++it )
400
// QString language = TalkerCode::languageCodeToLanguage(*it);
401
// m_languagesToCodes[language] = *it;
404
// // All plugins support "Other".
405
// // TODO: Eventually, this should not be necessary, since all plugins will know
406
// // the languages they support and report them in call to getSupportedLanguages().
407
// if (!languageCodes.contains("other")) languageCodes.append("other");
409
// // Add supported language codes to synthesizer-to-language map.
410
// m_synthToLangMap[synthName] = languageCodes;
413
// Add "Other" language.
414
//m_languagesToCodes[i18nc("Other language", "Other")] = "other";
417
m_filterListModel.clear();
418
QStringList filterIDsList = generalConfig.readEntry ("FilterIDs", QStringList());
419
// kDebug() << "KCMKttsMgr::load: FilterIDs = " << filterIDsList;
420
if (!filterIDsList.isEmpty()) {
421
QStringList::ConstIterator itEnd = filterIDsList.constEnd();
422
for (QStringList::ConstIterator it = filterIDsList.constBegin(); it != itEnd; ++it) {
423
QString filterID = *it;
424
// kDebug() << "KCMKttsMgr::load: filterID = " << filterID;
425
KConfigGroup filterConfig (m_config, QLatin1String( "Filter_" ) + filterID);
426
QString desktopEntryName = filterConfig.readEntry ("DesktopEntryName", QString());
427
// If a DesktopEntryName is not in the config file, it was configured before
428
// we started using them, when we stored translated plugin names instead.
429
// Try to convert the translated plugin name to a DesktopEntryName.
430
// DesktopEntryNames are better because user can change their desktop language
431
// and DesktopEntryName won't change.
432
QString filterPlugInName;
433
filterPlugInName = FilterDesktopEntryNameToName (desktopEntryName);
434
if (!filterPlugInName.isEmpty()) {
437
fi.plugInName = filterPlugInName;
438
fi.desktopEntryName = desktopEntryName;
439
fi.userFilterName = filterConfig.readEntry ("UserFilterName", filterPlugInName);
440
fi.multiInstance = filterConfig.readEntry ("MultiInstance", false);
441
fi.enabled = filterConfig.readEntry ("Enabled", false);
442
// Determine if this filter is a Sentence Boundary Detector (SBD).
443
m_filterListModel.appendRow (fi);
444
if (filterID.toInt() > m_lastFilterID) m_lastFilterID = filterID.toInt();
449
// Add at least one unchecked instance of each available filter plugin if there is
450
// not already at least one instance and the filter can autoconfig itself.
451
//offers = KServiceTypeTrader::self()->query("KTTSD/FilterPlugin");
452
//for (int i=0; i < offers.count() ; ++i)
454
// QString filterPlugInName = offers[i]->name();
455
// QString desktopEntryName = FilterNameToDesktopEntryName(filterPlugInName);
456
// if (!desktopEntryName.isEmpty() && (countFilterPlugins(filterPlugInName) == 0))
458
// // Must load plugin to determine if it supports multiple instances
459
// // and to see if it can autoconfigure itself.
460
// KttsFilterConf* filterPlugIn = loadFilterPlugin(desktopEntryName);
464
// QString filterID = QString::number(m_lastFilterID);
465
// QString groupName = "Filter_" + filterID;
466
// filterPlugIn->load(m_config, groupName);
467
// QString userFilterName = filterPlugIn->userPlugInName();
468
// if (!userFilterName.isEmpty())
470
// kDebug() << "KCMKttsMgr::load: auto-configuring filter " << userFilterName;
471
// bool multiInstance = filterPlugIn->supportsMultiInstance();
474
// fi.userFilterName = userFilterName;
475
// fi.plugInName = filterPlugInName;
476
// fi.desktopEntryName = desktopEntryName;
477
// fi.enabled = true;
478
// fi.multiInstance = multiInstance;
479
// // Determine if plugin is an SBD filter.
480
// bool isSbd = filterPlugIn->isSBD();
482
// m_sbdFilterListModel.appendRow(fi);
484
// m_filterListModel.appendRow(fi);
485
// KConfigGroup filterConfig(m_config, groupName);
486
// filterPlugIn->save(m_config, groupName);
487
// filterConfig.writeEntry("DesktopEntryName", desktopEntryName);
488
// filterConfig.writeEntry("UserFilterName", userFilterName);
489
// filterConfig.writeEntry("Enabled", isSbd);
490
// filterConfig.writeEntry("MultiInstance", multiInstance);
491
// filterConfig.writeEntry("IsSBD", isSbd);
492
// filterIDsList.append(filterID);
493
// } else m_lastFilterID--;
495
// kDebug() << "KCMKttsMgr::load: Unable to load filter plugin " << filterPlugInName
496
// << " DesktopEntryName " << desktopEntryName << endl;
497
// delete filterPlugIn;
500
// Rewrite list of FilterIDs in case we added any.
501
QString filterIDs = filterIDsList.join (QLatin1String( "," ));
502
generalConfig.writeEntry ("FilterIDs", filterIDs);
505
// Update controls based on new states.
506
updateTalkerButtons();
507
updateFilterButtons();
510
m_suppressConfigChanged = false;
514
* This function gets called when the user wants to save the settings in
515
* the user interface, updating the config files or wherever the
516
* configuration is stored. The method is called when the user clicks "Apply"
519
void KCMKttsMgr::save()
521
// kDebug() << "KCMKttsMgr::save: Running";
525
m_config->deleteGroup ("General", 0);
527
// Set the group general for the configuration of kttsd itself (no plug ins)
528
KConfigGroup generalConfig (m_config, "General");
530
// Uncheck and disable KTTSD checkbox if no Talkers are configured.
531
// Enable checkbox if at least one Talker is configured.
532
bool enableJovieWasToggled = false;
533
//if (m_talkerListModel.rowCount() == 0)
535
//enableKttsdWasToggled = enableKttsdCheckBox->isChecked();
536
//enableKttsdCheckBox->setChecked(false);
537
//enableKttsdCheckBox->setEnabled(false);
538
// Might as well zero LastTalkerID as well.
539
//m_lastTalkerID = 0;
542
enableJovieCheckBox->setEnabled (true);
544
generalConfig.writeEntry ("EnableKttsd", enableJovieCheckBox->isChecked());
546
// Get ordered list of all talker IDs.
547
QList<TalkerCode> talkers;
548
QStringList talkerIDsList;
549
KConfigGroup talkerGroup (m_config, "Talkers");
550
talkerGroup.deleteGroup();
551
for (int i = 0; i < m_talkerListModel.rowCount(); ++i) {
552
TalkerCode talker = m_talkerListModel.getRow (i);
554
talkerGroup.writeEntry (talker.name(), talker.getTalkerCode());
555
talkerIDsList << talker.name();
558
QString talkerIDs = talkerIDsList.join (QLatin1String( "," ));
559
generalConfig.writeEntry ("TalkerIDs", talkerIDs);
561
// Erase obsolete Talker_nn sections.
562
QStringList groupList = m_config->groupList();
563
int groupListCount = groupList.count();
564
for (int groupNdx = 0; groupNdx < groupListCount; ++groupNdx) {
565
QString groupName = groupList[groupNdx];
566
if (groupName.left (7) == QLatin1String( "Talker_" )) {
567
QString groupTalkerID = groupName.mid (7);
568
if (!talkerIDsList.contains (groupTalkerID))
569
m_config->deleteGroup (groupName, 0);
573
// Get ordered list of all filter IDs. Record enabled state of each filter.
574
QStringList filterIDsList;
575
for (int i = 0; i < m_filterListModel.rowCount(); ++i) {
576
FilterItem fi = m_filterListModel.getRow (i);
577
filterIDsList.append (fi.id);
578
KConfigGroup filterConfig (m_config, QLatin1String( "Filter_" ) + fi.id);
579
filterConfig.writeEntry ("Enabled", fi.enabled);
581
QString filterIDs = filterIDsList.join (QLatin1String( "," ));
582
generalConfig.writeEntry ("FilterIDs", filterIDs);
584
// Erase obsolete Filter_nn sections.
585
for (int groupNdx = 0; groupNdx < groupListCount; ++groupNdx) {
586
QString groupName = groupList[groupNdx];
587
if (groupName.left (7) == QLatin1String( "Filter_" )) {
588
QString groupFilterID = groupName.mid (7);
589
if (!filterIDsList.contains (groupFilterID))
590
m_config->deleteGroup (groupName, 0);
596
// apply changes in the jobs page if it exists
597
if (m_jobMgrWidget) {
598
m_jobMgrWidget->save();
601
// If we automatically unchecked the Enable KTTSD checkbox, stop KTTSD.
602
if (enableJovieWasToggled)
603
slotEnableJovie_toggled (false);
605
// If Jovie is running, reinitialize it.
607
kDebug() << "Restarting Jovie";
613
void KCMKttsMgr::slotTabChanged()
615
// TODO: Commentting this out to avoid a crash. It seems there is a bug in
616
// KDialog. The workaround is to call setDefaultButton(), but that method
617
// is not available to a KCModule. Uncomment this when bug is fixed.
618
// setButtons(buttons());
619
int currentPageIndex = mainTab->currentIndex();
620
if (currentPageIndex == wpJobs) {
622
KMessageBox::information (this,
623
i18n ("You have made changes to the configuration but have not saved them yet. "
624
"Click Apply to save the changes or Cancel to abandon the changes."));
630
* This function is called to set the settings in the module to sensible
631
* default values. It gets called when hitting the "Default" button. The
632
* default values should probably be the same as the ones the application
633
* uses when started without a config file.
635
void KCMKttsMgr::defaults()
637
// kDebug() << "Running: KCMKttsMgr::defaults: Running";
639
int currentPageIndex = mainTab->currentIndex();
644
* This is a static method which gets called to realize the modules settings
645
* during the startup of KDE. NOTE that most modules do not implement this
646
* method, but modules like the keyboard and mouse modules, which directly
647
* interact with the X-server, need this method. As this method is static,
648
* it can avoid to create an instance of the user interface, which is often
649
* not needed in this case.
651
void KCMKttsMgr::init()
653
// kDebug() << "KCMKttsMgr::init: Running";
657
* This function returns the small quickhelp.
658
* That is displayed in the sidebar in the KControl
660
QString KCMKttsMgr::quickHelp() const
662
// kDebug() << "KCMKttsMgr::quickHelp: Running";
664
"<h1>Text-to-Speech</h1>"
665
"<p>This is the configuration for the text-to-speech D-Bus service</p>"
666
"<p>This allows other applications to access text-to-speech resources</p>"
667
"<p>Be sure to configure a default language for the language you are using as this will be the language used by most of the applications</p>");
670
const KAboutData* KCMKttsMgr::aboutData() const
673
new KAboutData (I18N_NOOP ("jovie"), 0, ki18n ("KCMKttsMgr"),
674
0, KLocalizedString(), KAboutData::License_GPL,
675
ki18n ("(c) 2010, Jeremy Whiting"));
677
about->addAuthor (ki18n ("José Pablo Ezequiel Fernández"), ki18n ("Author") , "pupeno@kde.org");
678
about->addAuthor (ki18n ("Gary Cramblitt"), ki18n ("Maintainer") , "garycramblitt@comcast.net");
679
about->addAuthor (ki18n ("Olaf Schmidt"), ki18n ("Contributor"), "ojschmidt@kde.org");
680
about->addAuthor (ki18n ("Paul Giannaros"), ki18n ("Contributor"), "ceruleanblaze@gmail.com");
686
* Loads the configuration plug in for a named filter plug in.
687
* @param plugInName DesktopEntryName of the plugin.
688
* @return Pointer to the configuration plugin for the Filter.
690
KttsFilterConf* KCMKttsMgr::loadFilterPlugin (const QString& plugInName)
692
// kDebug() << "KCMKttsMgr::loadPlugin: Running";
695
KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" ),
696
QString (QLatin1String( "DesktopEntryName == '%1'" )).arg (plugInName));
698
if (offers.count() == 1) {
699
// When the entry is found, load the plug in
700
// First create a factory for the library
701
KLibFactory *factory = KLibLoader::self()->factory (QLatin1String( offers[0]->library().toLatin1() ));
703
// If the factory is created successfully, instantiate the KttsFilterConf class for the
704
// specific plug in to get the plug in configuration object.
706
KttsFilterConf *plugIn =
707
KLibLoader::createInstance<KttsFilterConf> (
708
QLatin1String( offers[0]->library().toLatin1() ), NULL, QStringList ( QLatin1String( offers[0]->library().toLatin1() )),
711
// If everything went ok, return the plug in pointer.
714
// Something went wrong, returning null.
715
kDebug() << "KCMKttsMgr::loadFilterPlugin: Unable to instantiate KttsFilterConf class for plugin " << plugInName << " error: " << errorNo;
719
// Something went wrong, returning null.
720
kDebug() << "KCMKttsMgr::loadFilterPlugin: Unable to create Factory object for plugin " << plugInName;
724
// The plug in was not found (unexpected behaviour, returns null).
725
kDebug() << "KCMKttsMgr::loadFilterPlugin: KTrader did not return an offer for plugin " << plugInName;
732
void KCMKttsMgr::slotAddTalkerButton_clicked()
734
QPointer<AddTalker> dlg = new AddTalker (this);
735
if (dlg->exec() == QDialog::Accepted) {
736
TalkerCode code = dlg->getTalkerCode();
738
// Add to list of Talkers.
739
m_talkerListModel.appendRow (code);
741
// Make sure visible.
742
const QModelIndex modelIndex = m_talkerListModel.index (m_talkerListModel.rowCount(),
744
talkersView->scrollTo (modelIndex);
746
// Select the new item, update buttons.
747
talkersView->setCurrentIndex (modelIndex);
748
updateTalkerButtons();
750
// Inform Control Center that change has been made.
755
kDebug() << "KCMKttsMgr::addTalker: done.";
758
void KCMKttsMgr::slotAddFilterButton_clicked()
766
void KCMKttsMgr::addFilter()
768
QTreeView* lView = filtersView;
769
FilterListModel* model = qobject_cast<FilterListModel *> (lView->model());
771
// Build a list of filters that support multiple instances and let user choose.
772
QStringList filterPlugInNames;
773
for (int i = 0; i < model->rowCount(); ++i) {
774
FilterItem fi = model->getRow (i);
775
if (fi.multiInstance) {
776
if (!filterPlugInNames.contains (fi.plugInName))
777
filterPlugInNames.append (fi.plugInName);
780
// Append those available plugins not yet in the list at all.
781
KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" ));
782
for (int i = 0; i < offers.count() ; ++i) {
783
QString filterPlugInName = offers[i]->name();
784
if (countFilterPlugins (filterPlugInName) == 0) {
785
QString desktopEntryName = FilterNameToDesktopEntryName (filterPlugInName);
786
KttsFilterConf* filterConf = loadFilterPlugin (desktopEntryName);
788
filterPlugInNames.append (filterPlugInName);
794
// If no choice (shouldn't happen), bail out.
795
// kDebug() << "KCMKttsMgr::addFilter: filterPluginNames = " << filterPlugInNames;
796
if (filterPlugInNames.count() == 0) return;
798
// If exactly one choice, skip selection dialog, otherwise display list to user to select from.
799
bool okChosen = false;
800
QString filterPlugInName;
801
if (filterPlugInNames.count() > 1) {
802
filterPlugInName = KInputDialog::getItem (
803
i18n ("Select Filter"),
810
if (!okChosen) return;
812
filterPlugInName = filterPlugInNames[0];
814
// kDebug() << "KCMKttsMgr::addFilter: filterPlugInName = " << filterPlugInName;
816
// Assign a new Filter ID for the filter. Wraps around to 1.
817
QString filterID = QString::number (m_lastFilterID + 1);
819
// Erase extraneous Filter configuration entries that might be there.
820
m_config->deleteGroup (QLatin1String ("Filter_") + filterID, 0);
823
// Get DesktopEntryName from the translated name.
824
QString desktopEntryName = FilterNameToDesktopEntryName (filterPlugInName);
825
// This shouldn't happen, but just in case.
826
if (desktopEntryName.isEmpty()) return;
829
m_loadedFilterPlugIn = loadFilterPlugin (desktopEntryName);
830
if (!m_loadedFilterPlugIn) return;
832
// Permit plugin to autoconfigure itself.
833
m_loadedFilterPlugIn->load (m_config, QLatin1String ("Filter_") + filterID);
835
// Display configuration dialog for user to configure the plugin.
839
if (!m_loadedFilterPlugIn) {
845
// Get user's name for Filter.
846
QString userFilterName = m_loadedFilterPlugIn->userPlugInName();
848
// If user properly configured the plugin, save its configuration.
849
if (!userFilterName.isEmpty()) {
850
// Let plugin save its configuration.
851
m_loadedFilterPlugIn->save (m_config, QLatin1String ("Filter_" ) + filterID);
853
// Record last Filter ID used for next add.
854
m_lastFilterID = filterID.toInt();
856
// Determine if filter supports multiple instances.
857
bool multiInstance = m_loadedFilterPlugIn->supportsMultiInstance();
859
// Record configuration data. Note, might as well do this now.
860
KConfigGroup filterConfig (m_config, QLatin1String ("Filter_" ) + filterID);
861
filterConfig.writeEntry ("DesktopEntryName", desktopEntryName);
862
filterConfig.writeEntry ("UserFilterName", userFilterName);
863
filterConfig.writeEntry ("MultiInstance", multiInstance);
864
filterConfig.writeEntry ("Enabled", true);
867
// Add listview item.
870
fi.plugInName = filterPlugInName;
871
fi.userFilterName = userFilterName;
872
fi.desktopEntryName = desktopEntryName;
873
fi.multiInstance = multiInstance;
875
model->appendRow (fi);
877
// Make sure visible.
878
QModelIndex modelIndex = model->index (model->rowCount() - 1, 0, QModelIndex());
879
lView->scrollTo (modelIndex);
881
// Select the new item, update buttons.
882
lView->setCurrentIndex (modelIndex);
883
updateFilterButtons();
885
// Inform Control Center that change has been made.
889
// Don't need plugin in memory anymore.
890
delete m_loadedFilterPlugIn;
891
m_loadedFilterPlugIn = 0;
895
// kDebug() << "KCMKttsMgr::addFilter: done.";
901
void KCMKttsMgr::slotRemoveTalkerButton_clicked()
903
// kDebug() << "KCMKttsMgr::removeTalker: Running";
905
// Get the selected talker.
906
QModelIndex modelIndex = talkersView->currentIndex();
907
if (!modelIndex.isValid()) return;
909
// Delete the talker from configuration file?
910
QString talkerID = m_talkerListModel.getRow (modelIndex.row()).name();
911
m_config->deleteGroup (QLatin1String ("Talker_") + talkerID, 0);
913
// Delete the talker from the list of Talkers.
914
m_talkerListModel.removeRow (modelIndex.row());
916
updateTalkerButtons();
918
// Emit configuration changed.
922
void KCMKttsMgr::slotRemoveFilterButton_clicked()
930
void KCMKttsMgr::removeFilter()
932
// kDebug() << "KCMKttsMgr::removeFilter: Running";
934
FilterListModel* model;
935
QTreeView* lView = filtersView;
936
model = qobject_cast<FilterListModel *> (lView->model());
937
QModelIndex modelIndex = lView->currentIndex();
938
if (!modelIndex.isValid()) return;
939
QString filterID = model->getRow (modelIndex.row()).id;
940
// Delete the filter from list view.
941
model->removeRow (modelIndex.row());
942
updateFilterButtons();
944
// Delete the filter from the configuration file?
945
kDebug() << "KCMKttsMgr::removeFilter: removing FilterID = " << filterID << " from config file.";
946
m_config->deleteGroup (QLatin1String ("Filter_") + filterID, 0);
948
// Emit configuration changed.
952
void KCMKttsMgr::slotHigherTalkerPriorityButton_clicked()
954
QModelIndex modelIndex = talkersView->currentIndex();
955
if (!modelIndex.isValid()) return;
956
m_talkerListModel.swap (modelIndex.row(), modelIndex.row() - 1);
957
modelIndex = m_talkerListModel.index (modelIndex.row() - 1, 0, QModelIndex());
958
talkersView->scrollTo (modelIndex);
959
talkersView->setCurrentIndex (modelIndex);
960
updateTalkerButtons();
964
void KCMKttsMgr::slotHigherFilterPriorityButton_clicked()
966
QModelIndex modelIndex = filtersView->currentIndex();
967
if (!modelIndex.isValid()) return;
968
m_filterListModel.swap (modelIndex.row(), modelIndex.row() - 1);
969
modelIndex = m_filterListModel.index (modelIndex.row() - 1, 0, QModelIndex());
970
filtersView->scrollTo (modelIndex);
971
filtersView->setCurrentIndex (modelIndex);
972
updateFilterButtons();
976
void KCMKttsMgr::slotLowerTalkerPriorityButton_clicked()
978
QModelIndex modelIndex = talkersView->currentIndex();
979
if (!modelIndex.isValid()) return;
980
m_talkerListModel.swap (modelIndex.row(), modelIndex.row() + 1);
981
modelIndex = m_talkerListModel.index (modelIndex.row() + 1, 0, QModelIndex());
982
talkersView->scrollTo (modelIndex);
983
talkersView->setCurrentIndex (modelIndex);
984
updateTalkerButtons();
988
void KCMKttsMgr::slotLowerFilterPriorityButton_clicked()
990
QModelIndex modelIndex = filtersView->currentIndex();
991
if (!modelIndex.isValid()) return;
992
m_filterListModel.swap (modelIndex.row(), modelIndex.row() + 1);
993
modelIndex = m_filterListModel.index (modelIndex.row() + 1, 0, QModelIndex());
994
filtersView->scrollTo (modelIndex);
995
filtersView->setCurrentIndex (modelIndex);
996
updateFilterButtons();
1001
* Update the status of the Talker buttons.
1003
void KCMKttsMgr::updateTalkerButtons()
1005
// kDebug() << "KCMKttsMgr::updateTalkerButtons: Running";
1006
QModelIndex modelIndex = talkersView->currentIndex();
1007
if (modelIndex.isValid()) {
1008
removeTalkerButton->setEnabled (true);
1009
configureTalkerButton->setEnabled (true);
1010
higherTalkerPriorityButton->setEnabled (modelIndex.row() != 0);
1011
lowerTalkerPriorityButton->setEnabled (modelIndex.row() < (m_talkerListModel.rowCount() - 1));
1013
removeTalkerButton->setEnabled (false);
1014
configureTalkerButton->setEnabled (false);
1015
higherTalkerPriorityButton->setEnabled (false);
1016
lowerTalkerPriorityButton->setEnabled (false);
1018
// kDebug() << "KCMKttsMgr::updateTalkerButtons: Exiting";
1022
* Update the status of the normal Filter buttons.
1024
void KCMKttsMgr::updateFilterButtons()
1026
// kDebug() << "KCMKttsMgr::updateFilterButtons: Running";
1027
QModelIndex modelIndex = filtersView->currentIndex();
1028
if (modelIndex.isValid()) {
1029
removeFilterButton->setEnabled (true);
1030
configureFilterButton->setEnabled (true);
1031
higherFilterPriorityButton->setEnabled (modelIndex.row() != 0);
1032
lowerFilterPriorityButton->setEnabled (modelIndex.row() < (m_filterListModel.rowCount() - 1));
1034
removeFilterButton->setEnabled (false);
1035
configureFilterButton->setEnabled (false);
1036
higherFilterPriorityButton->setEnabled (false);
1037
lowerFilterPriorityButton->setEnabled (false);
1039
// kDebug() << "KCMKttsMgr::updateFilterButtons: Exiting";
1043
* This signal is emitted whenever user checks/unchecks the Enable TTS System check box.
1045
void KCMKttsMgr::slotEnableJovie_toggled (bool)
1047
// Prevent re-entrancy.
1048
static bool reenter;
1049
if (reenter) return;
1051
// See if Jovie is running.
1052
bool kttsdRunning = (QDBusConnection::sessionBus().interface()->isServiceRegistered (QLatin1String( "org.kde.jovie" )));
1054
// kDebug() << "KCMKttsMgr::slotEnableKttsd_toggled: kttsdRunning = " << kttsdRunning;
1055
// If Enable Jovie check box is checked and it is not running, then start Jovie.
1056
if (enableJovieCheckBox->isChecked()) {
1057
if (!kttsdRunning) {
1058
// kDebug() << "KCMKttsMgr::slotEnableKttsd_toggled:: Starting Jovie";
1060
if (KToolInvocation::startServiceByDesktopName (QLatin1String( "jovie" ), QStringList(), &error)) {
1061
kDebug() << "Starting Jovie failed with message " << error;
1062
enableJovieCheckBox->setChecked (false);
1070
// If check box is not checked and it is running, then stop Jovie.
1073
// kDebug() << "KCMKttsMgr::slotEnableKttsd_toggled:: Stopping Jovie";
1075
m_kspeech = new OrgKdeKSpeechInterface (QLatin1String( "org.kde.jovie" ), QLatin1String( "/KSpeech" ), QDBusConnection::sessionBus());
1076
m_kspeech->kttsdExit();
1086
* This slot is called whenever KTTSD starts or restarts.
1088
void KCMKttsMgr::jovieStarted()
1090
// kDebug() << "KCMKttsMgr::jovieStarted: Running";
1091
bool kttsdLoaded = (m_jobMgrWidget != 0);
1092
// Load Job Manager Part library.
1094
m_jobMgrWidget = new KttsJobMgr (this);
1095
if (m_jobMgrWidget) {
1096
connect (m_jobMgrWidget, SIGNAL (configChanged()), this, SLOT (configChanged()));
1097
// Add the Job Manager part as a new tab.
1098
mainTab->addTab (m_jobMgrWidget, i18n ("Jobs"));
1101
kDebug() << "KCMKttsMgr::jovieStarted: Could not create kttsjobmgr part.";
1103
// Check/Uncheck the Enable KTTSD check box.
1105
enableJovieCheckBox->setChecked (true);
1106
m_kspeech = new OrgKdeKSpeechInterface (QLatin1String( "org.kde.jovie" ), QLatin1String( "/KSpeech" ), QDBusConnection::sessionBus());
1107
m_kspeech->setParent (this);
1108
m_kspeech->setApplicationName (QLatin1String( "KCMKttsMgr" ));
1109
m_kspeech->setIsSystemManager (true);
1110
// Connect KTTSD DBUS signals to our slots.
1111
connect (m_kspeech, SIGNAL (kttsdStarted()),
1112
this, SLOT (jovieStarted()));
1113
connect (m_kspeech, SIGNAL (kttsdExiting()),
1114
this, SLOT (jovieExiting()));
1115
connect (QDBusConnection::sessionBus().interface(), SIGNAL (serviceUnregistered(QString)),
1116
this, SLOT (slotServiceUnregistered(QString)));
1117
connect (QDBusConnection::sessionBus().interface(), SIGNAL (serviceOwnerChanged(QString,QString,QString)),
1118
this, SLOT (slotServiceOwnerChanged(QString,QString,QString)));
1120
kttsdVersion->setText (i18n ("Jovie Version: %1", m_kspeech->version()));
1123
enableJovieCheckBox->setChecked (false);
1129
void KCMKttsMgr::slotServiceUnregistered (const QString &service)
1131
if (service == QLatin1String ("org.kde.jovie")) {
1137
void KCMKttsMgr::slotServiceOwnerChanged (const QString &service, const QString &, const QString &newOwner)
1139
if (service == QLatin1String ("org.kde.jovie") && newOwner.isEmpty()) {
1145
* This slot is called whenever KTTSD is about to exit.
1147
void KCMKttsMgr::jovieExiting()
1149
// kDebug() << "KCMKttsMgr::kttsdExiting: Running";
1150
if (m_jobMgrWidget) {
1151
mainTab->removeTab (wpJobs);
1152
delete m_jobMgrWidget;
1156
enableJovieCheckBox->setChecked (false);
1157
disconnect (QDBusConnection::sessionBus().interface(), 0, this, 0);
1160
kttsdVersion->setText (i18n ("Jovie not running"));
1164
* User has requested display of talker configuration dialog.
1166
void KCMKttsMgr::slotConfigureTalkerButton_clicked()
1168
// Get highlighted Talker from ListView.
1169
QModelIndex modelIndex = talkersView->currentIndex();
1171
if (!modelIndex.isValid())
1174
TalkerCode tc = m_talkerListModel.getRow(modelIndex.row());
1176
QPointer<AddTalker> dlg = new AddTalker (this);
1177
dlg->setTalkerCode(tc);
1178
if (dlg->exec() == QDialog::Accepted) {
1179
TalkerCode code = dlg->getTalkerCode();
1181
// Change the existing talker code to this new code.
1182
m_talkerListModel.updateRow(modelIndex.row(), code);
1188
void KCMKttsMgr::slotConfigureFilterButton_clicked()
1190
configureFilterItem();
1194
* User has requested display of filter configuration dialog.
1196
void KCMKttsMgr::configureFilterItem()
1198
// Get highlighted plugin from Filter ListView and load into memory.
1200
FilterListModel* model;
1201
lView = filtersView;
1202
model = &m_filterListModel;
1203
QModelIndex modelIndex = lView->currentIndex();
1204
if (!modelIndex.isValid()) return;
1205
FilterItem fi = model->getRow (modelIndex.row());
1206
QString filterID = fi.id;
1207
QString filterPlugInName = fi.plugInName;
1208
QString desktopEntryName = fi.desktopEntryName;
1209
if (desktopEntryName.isEmpty()) return;
1210
m_loadedFilterPlugIn = loadFilterPlugin (desktopEntryName);
1211
if (!m_loadedFilterPlugIn) return;
1212
// kDebug() << "KCMKttsMgr::slot_configureFilter: plugin for " << filterPlugInName << " loaded successfully.";
1214
// Tell plugin to load its configuration.
1215
// kDebug() << "KCMKttsMgr::slot_configureFilter: about to call plugin load() method with Filter ID = " << filterID;
1216
m_loadedFilterPlugIn->load (m_config, QLatin1String ("Filter_") + filterID);
1218
// Display configuration dialog.
1222
if (!m_loadedFilterPlugIn) {
1228
// Get user's name for the plugin.
1229
QString userFilterName = m_loadedFilterPlugIn->userPlugInName();
1231
// If user properly configured the plugin, save the configuration.
1232
if (!userFilterName.isEmpty()) {
1233
// Let plugin save its configuration.
1234
m_loadedFilterPlugIn->save (m_config, QLatin1String ("Filter_") + filterID);
1236
// Save configuration.
1237
KConfigGroup filterConfig (m_config, QLatin1String ("Filter_") + filterID);
1238
filterConfig.writeEntry ("DesktopEntryName", desktopEntryName);
1239
filterConfig.writeEntry ("UserFilterName", userFilterName);
1240
filterConfig.writeEntry ("Enabled", true);
1241
filterConfig.writeEntry ("MultiInstance", m_loadedFilterPlugIn->supportsMultiInstance());
1248
fi.desktopEntryName = desktopEntryName;
1249
fi.userFilterName = userFilterName;
1251
fi.multiInstance = m_loadedFilterPlugIn->supportsMultiInstance();
1252
model->updateRow (modelIndex.row(), fi);
1253
// Inform Control Center that configuration has changed.
1262
* Display filter configuration dialog. The plugin is assumed already loaded into
1263
* memory referenced by m_loadedFilterPlugIn.
1265
void KCMKttsMgr::configureFilter()
1267
if (!m_loadedFilterPlugIn) return;
1268
m_configDlg = new KDialog (this);
1269
m_configDlg->setCaption (i18n ("Filter Configuration"));
1270
m_configDlg->setButtons (KDialog::Help | KDialog::Default | KDialog::Ok | KDialog::Cancel);
1271
m_configDlg->setDefaultButton (KDialog::Cancel);
1272
m_loadedFilterPlugIn->setMinimumSize (m_loadedFilterPlugIn->minimumSizeHint());
1273
m_loadedFilterPlugIn->show();
1274
m_configDlg->setMainWidget (m_loadedFilterPlugIn);
1275
m_configDlg->setHelp (QLatin1String( "configure-filter" ), QLatin1String( "jovie" ));
1276
m_configDlg->enableButtonOk (false);
1277
connect (m_loadedFilterPlugIn, SIGNAL (changed(bool)), this, SLOT (slotConfigFilterDlg_ConfigChanged()));
1278
connect (m_configDlg, SIGNAL (defaultClicked()), this, SLOT (slotConfigFilterDlg_DefaultClicked()));
1279
connect (m_configDlg, SIGNAL (cancelClicked()), this, SLOT (slotConfigFilterDlg_CancelClicked()));
1280
// Display the dialog.
1281
m_configDlg->exec();
1285
* Count number of configured Filters with the specified plugin name.
1287
int KCMKttsMgr::countFilterPlugins (const QString& filterPlugInName)
1290
for (int i = 0; i < m_filterListModel.rowCount(); ++i) {
1291
FilterItem fi = m_filterListModel.getRow (i);
1292
if (fi.plugInName == filterPlugInName) ++cnt;
1297
void KCMKttsMgr::slotConfigTalkerDlg_ConfigChanged()
1299
//m_configDlg->enableButtonOk(!m_loadedTalkerPlugIn->getTalkerCode().isEmpty());
1302
void KCMKttsMgr::slotConfigFilterDlg_ConfigChanged()
1304
m_configDlg->enableButtonOk (!m_loadedFilterPlugIn->userPlugInName().isEmpty());
1307
void KCMKttsMgr::slotConfigTalkerDlg_DefaultClicked()
1309
//m_loadedTalkerPlugIn->defaults();
1312
void KCMKttsMgr::slotConfigFilterDlg_DefaultClicked()
1314
m_loadedFilterPlugIn->defaults();
1317
void KCMKttsMgr::slotConfigTalkerDlg_CancelClicked()
1319
//delete m_loadedTalkerPlugIn;
1320
//m_loadedTalkerPlugIn = 0;
1323
void KCMKttsMgr::slotConfigFilterDlg_CancelClicked()
1325
delete m_loadedFilterPlugIn;
1326
m_loadedFilterPlugIn = 0;
1330
* Uses KTrader to convert a translated Filter Plugin Name to DesktopEntryName.
1331
* @param name The translated plugin name. From Name= line in .desktop file.
1332
* @return DesktopEntryName. The name of the .desktop file (less .desktop).
1333
* QString() if not found.
1335
QString KCMKttsMgr::FilterNameToDesktopEntryName (const QString& name)
1337
if (name.isEmpty()) return QString();
1338
const KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" ));
1339
for (int ndx = 0; ndx < offers.count(); ++ndx)
1340
if (offers[ndx]->name() == name)
1341
return offers[ndx]->desktopEntryName();
1346
* Uses KTrader to convert a DesktopEntryName into a translated Filter Plugin Name.
1347
* @param desktopEntryName The DesktopEntryName.
1348
* @return The translated Name of the plugin, from Name= line in .desktop file.
1350
QString KCMKttsMgr::FilterDesktopEntryNameToName (const QString& desktopEntryName)
1352
if (desktopEntryName.isEmpty()) return QString();
1353
KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" ),
1354
QString (QLatin1String( "DesktopEntryName == '%1'" )).arg (desktopEntryName));
1356
if (offers.count() == 1)
1357
return offers[0]->name();
1363
void KCMKttsMgr::slotFilterListView_clicked (const QModelIndex & index)
1365
if (!index.isValid()) return;
1366
if (index.column() != 0) return;
1367
if (index.row() < 0 || index.row() >= m_filterListModel.rowCount()) return;
1368
FilterItem fi = m_filterListModel.getRow (index.row());
1369
fi.enabled = !fi.enabled;
1370
m_filterListModel.updateRow (index.row(), fi);