/***************************************************** vim:set ts=4 sw=4 sts=4: KControl module for KTTSD configuration and Job Management ------------------- Copyright 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández Copyright 2004-2005 by Gary Cramblitt Copyright 2009 by Jeremy Whiting ------------------- Original author: José Pablo Ezequiel "Pupeno" Fernández Current Maintainer: Jeremy Whiting 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. ***************************************************************************/ // Note to programmers. There is a subtle difference between a plugIn name and a // synthesizer name. The latter is a translated name, for example, "Festival Interactivo", // while the former is alway an English name, example "Festival Interactive". // KCMKttsMgr includes. #include "kcmkttsmgr.h" #include "moc_kcmkttsmgr.cpp" // C++ includes. #include // Qt includes. #include #include #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KTTS includes. #include "talkercode.h" #include "filterconf.h" #include "kttsjobmgr.h" // Some constants. // Defaults set when clicking Defaults button. const bool autostartMgrCheckBoxValue = true; const bool autoexitMgrCheckBoxValue = true; // Make this a plug in. K_PLUGIN_FACTORY (KCMKttsMgrFactory, registerPlugin();) K_EXPORT_PLUGIN (KCMKttsMgrFactory ("jovie")) // ---------------------------------------------------------------------------- FilterListModel::FilterListModel (FilterList filters, QObject *parent) : QAbstractListModel (parent), m_filters (filters) { } int FilterListModel::rowCount (const QModelIndex &parent) const { if (!parent.isValid()) return m_filters.count(); else return 0; } int FilterListModel::columnCount (const QModelIndex &parent) const { Q_UNUSED (parent); return 2; } QModelIndex FilterListModel::index (int row, int column, const QModelIndex &parent) const { if (!parent.isValid()) return createIndex (row, column, 0); else return QModelIndex(); } QModelIndex FilterListModel::parent (const QModelIndex &index) const { Q_UNUSED (index); return QModelIndex(); } QVariant FilterListModel::data (const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() < 0 || index.row() >= m_filters.count()) return QVariant(); if (index.column() < 0 || index.column() >= 2) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) switch (index.column()) { case 0: return QVariant(); break; case 1: return m_filters.at (index.row()).userFilterName; break; } if (role == Qt::CheckStateRole) switch (index.column()) { case 0: if (m_filters.at (index.row()).enabled) return Qt::Checked; else return Qt::Unchecked; break; case 1: return QVariant(); break; } return QVariant(); } Qt::ItemFlags FilterListModel::flags (const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemIsEnabled; switch (index.column()) { case 0: return QAbstractItemModel::flags (index) | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; break; case 1: return QAbstractItemModel::flags (index) | Qt::ItemIsEnabled | Qt::ItemIsSelectable; break; } return QAbstractItemModel::flags (index) | Qt::ItemIsEnabled; } QVariant FilterListModel::headerData (int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) switch (section) { case 0: return QLatin1String( "" ); case 1: return i18n ("Filter"); }; return QVariant(); } bool FilterListModel::removeRow (int row, const QModelIndex & parent) { beginRemoveRows (parent, row, row); m_filters.removeAt (row); endRemoveRows(); return true; } FilterItem FilterListModel::getRow (int row) const { if (row < 0 || row >= rowCount()) return FilterItem(); return m_filters[row]; } bool FilterListModel::appendRow (FilterItem& filter) { beginInsertRows (QModelIndex(), m_filters.count(), m_filters.count()); m_filters.append (filter); endInsertRows(); return true; } bool FilterListModel::updateRow (int row, FilterItem& filter) { m_filters.replace (row, filter); emit dataChanged (index (row, 0, QModelIndex()), index (row, columnCount() - 1, QModelIndex())); return true; } bool FilterListModel::swap (int i, int j) { m_filters.swap (i, j); emit dataChanged (index (i, 0, QModelIndex()), index (j, columnCount() - 1, QModelIndex())); return true; } void FilterListModel::clear() { m_filters.clear(); emit reset(); } // ---------------------------------------------------------------------------- /** * Constructor. */ KCMKttsMgr::KCMKttsMgr (QWidget *parent, const QVariantList &) : KCModule (KCMKttsMgrFactory::componentData(), parent/*, name*/), m_kspeech (0) { // kDebug() << "KCMKttsMgr constructor running."; // Initialize some variables. m_config = 0; m_jobMgrWidget = 0; m_configDlg = 0; m_changed = false; m_suppressConfigChanged = false; // Add the KTTS Manager widget setupUi (this); // Connect Views to Models and set row selection mode. talkersView->setModel (&m_talkerListModel); filtersView->setModel (&m_filterListModel); talkersView->setSelectionBehavior (QAbstractItemView::SelectRows); filtersView->setSelectionBehavior (QAbstractItemView::SelectRows); talkersView->setRootIsDecorated (false); filtersView->setRootIsDecorated (false); talkersView->setItemsExpandable (false); filtersView->setItemsExpandable (false); // Give buttons icons. // Talkers tab. higherTalkerPriorityButton->setIcon (KIcon ( QLatin1String( "go-up" ))); lowerTalkerPriorityButton->setIcon (KIcon ( QLatin1String( "go-down" ))); removeTalkerButton->setIcon (KIcon ( QLatin1String( "user-trash" ))); configureTalkerButton->setIcon (KIcon ( QLatin1String( "configure" ))); // Filters tab. higherFilterPriorityButton->setIcon (KIcon ( QLatin1String( "go-up" ))); lowerFilterPriorityButton->setIcon (KIcon ( QLatin1String( "go-down" ))); removeFilterButton->setIcon (KIcon ( QLatin1String( "user-trash" ))); configureFilterButton->setIcon (KIcon ( QLatin1String( "configure" ))); // Object for the KTTSD configuration. m_config = new KConfig (QLatin1String( "kttsdrc" )); // Connect the signals from the KCMKtssMgrWidget to this class. // General tab. connect (enableJovieCheckBox, SIGNAL (toggled(bool)), SLOT (slotEnableJovie_toggled(bool))); // Talker tab. connect (addTalkerButton, SIGNAL (clicked()), this, SLOT (slotAddTalkerButton_clicked())); connect (higherTalkerPriorityButton, SIGNAL (clicked()), this, SLOT (slotHigherTalkerPriorityButton_clicked())); connect (lowerTalkerPriorityButton, SIGNAL (clicked()), this, SLOT (slotLowerTalkerPriorityButton_clicked())); connect (removeTalkerButton, SIGNAL (clicked()), this, SLOT (slotRemoveTalkerButton_clicked())); connect (configureTalkerButton, SIGNAL (clicked()), this, SLOT (slotConfigureTalkerButton_clicked())); connect (talkersView, SIGNAL (clicked(QModelIndex)), this, SLOT (updateTalkerButtons())); // Filter tab. connect (addFilterButton, SIGNAL (clicked()), this, SLOT (slotAddFilterButton_clicked())); connect (higherFilterPriorityButton, SIGNAL (clicked()), this, SLOT (slotHigherFilterPriorityButton_clicked())); connect (lowerFilterPriorityButton, SIGNAL (clicked()), this, SLOT (slotLowerFilterPriorityButton_clicked())); connect (removeFilterButton, SIGNAL (clicked()), this, SLOT (slotRemoveFilterButton_clicked())); connect (configureFilterButton, SIGNAL (clicked()), this, SLOT (slotConfigureFilterButton_clicked())); connect (filtersView, SIGNAL (clicked(QModelIndex)), this, SLOT (updateFilterButtons())); connect (filtersView, SIGNAL (clicked(QModelIndex)), this, SLOT (slotFilterListView_clicked(QModelIndex))); // Others. connect (mainTab, SIGNAL (currentChanged(int)), this, SLOT (slotTabChanged())); // See if Jovie is already running, and if so, create jobs tab. if (QDBusConnection::sessionBus().interface()->isServiceRegistered (QLatin1String( "org.kde.jovie" ))) jovieStarted(); else // Start Jovie if check box is checked. slotEnableJovie_toggled (enableJovieCheckBox->isChecked()); // Adjust view column sizes. // TODO: To work properly, this needs to be done after the widgets are shown. // Possibly in Resize event? for (int i = 0; i < m_filterListModel.columnCount(); ++i) filtersView->resizeColumnToContents (i); for (int i = 0; i < m_talkerListModel.columnCount(); ++i) talkersView->resizeColumnToContents (i); // Switch to Talkers tab if none configured, // otherwise switch to Jobs tab if it is active. if (m_talkerListModel.rowCount() == 0) mainTab->setCurrentIndex (wpTalkers); else if (enableJovieCheckBox->isChecked()) mainTab->setCurrentIndex (wpJobs); } /** * Destructor. */ KCMKttsMgr::~KCMKttsMgr() { // kDebug() << "KCMKttsMgr::~KCMKttsMgr: Running"; delete m_config; } /** * 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 this is not called after the modules is loaded, * so you probably want to call this method in the constructor. */ void KCMKttsMgr::load() { // kDebug() << "KCMKttsMgr::load: Running"; m_changed = false; // Don't emit changed() signal while loading. m_suppressConfigChanged = true; // Set the group general for the configuration of kttsd itself (no plug ins) KConfigGroup generalConfig (m_config, "General"); // Overall settings. enableJovieCheckBox->setChecked (generalConfig.readEntry ("EnableJovie", enableJovieCheckBox->isChecked())); // Last filter ID. Used to generate a new ID for an added filter. m_lastFilterID = 0; // Load existing Talkers into Talker List. m_talkerListModel.loadTalkerCodesFromConfig (m_config); // Last talker ID. Used to generate a new ID for an added talker. //m_lastTalkerID = m_talkerListModel.highestTalkerId(); // Dictionary mapping languages to language codes. m_languagesToCodes.clear(); for (int i = 0; i < m_talkerListModel.rowCount(); ++i) { QString language = m_talkerListModel.getRow (i).language(); m_languagesToCodes[language] = language; } // Iterate thru the possible plug ins getting their language support codes. //for(int i=0; i < offers.count() ; ++i) //{ // QString synthName = offers[i]->name(); // QStringList languageCodes = offers[i]->property("X-KDE-Languages").toStringList(); // // Add language codes to the language-to-language code map. // QStringList::ConstIterator endLanguages(languageCodes.constEnd()); // for( QStringList::ConstIterator it = languageCodes.constBegin(); it != endLanguages; ++it ) // { // QString language = TalkerCode::languageCodeToLanguage(*it); // m_languagesToCodes[language] = *it; // } // // All plugins support "Other". // // TODO: Eventually, this should not be necessary, since all plugins will know // // the languages they support and report them in call to getSupportedLanguages(). // if (!languageCodes.contains("other")) languageCodes.append("other"); // // Add supported language codes to synthesizer-to-language map. // m_synthToLangMap[synthName] = languageCodes; //} // Add "Other" language. //m_languagesToCodes[i18nc("Other language", "Other")] = "other"; // Load Filters. m_filterListModel.clear(); QStringList filterIDsList = generalConfig.readEntry ("FilterIDs", QStringList()); // kDebug() << "KCMKttsMgr::load: FilterIDs = " << filterIDsList; if (!filterIDsList.isEmpty()) { QStringList::ConstIterator itEnd = filterIDsList.constEnd(); for (QStringList::ConstIterator it = filterIDsList.constBegin(); it != itEnd; ++it) { QString filterID = *it; // kDebug() << "KCMKttsMgr::load: filterID = " << filterID; KConfigGroup filterConfig (m_config, QLatin1String( "Filter_" ) + filterID); QString desktopEntryName = filterConfig.readEntry ("DesktopEntryName", QString()); // If a DesktopEntryName is not in the config file, it was configured before // we started using them, when we stored translated plugin names instead. // Try to convert the translated plugin name to a DesktopEntryName. // DesktopEntryNames are better because user can change their desktop language // and DesktopEntryName won't change. QString filterPlugInName; filterPlugInName = FilterDesktopEntryNameToName (desktopEntryName); if (!filterPlugInName.isEmpty()) { FilterItem fi; fi.id = filterID; fi.plugInName = filterPlugInName; fi.desktopEntryName = desktopEntryName; fi.userFilterName = filterConfig.readEntry ("UserFilterName", filterPlugInName); fi.multiInstance = filterConfig.readEntry ("MultiInstance", false); fi.enabled = filterConfig.readEntry ("Enabled", false); // Determine if this filter is a Sentence Boundary Detector (SBD). m_filterListModel.appendRow (fi); if (filterID.toInt() > m_lastFilterID) m_lastFilterID = filterID.toInt(); } } } // Add at least one unchecked instance of each available filter plugin if there is // not already at least one instance and the filter can autoconfig itself. //offers = KServiceTypeTrader::self()->query("KTTSD/FilterPlugin"); //for (int i=0; i < offers.count() ; ++i) //{ // QString filterPlugInName = offers[i]->name(); // QString desktopEntryName = FilterNameToDesktopEntryName(filterPlugInName); // if (!desktopEntryName.isEmpty() && (countFilterPlugins(filterPlugInName) == 0)) // { // // Must load plugin to determine if it supports multiple instances // // and to see if it can autoconfigure itself. // KttsFilterConf* filterPlugIn = loadFilterPlugin(desktopEntryName); // if (filterPlugIn) // { // ++m_lastFilterID; // QString filterID = QString::number(m_lastFilterID); // QString groupName = "Filter_" + filterID; // filterPlugIn->load(m_config, groupName); // QString userFilterName = filterPlugIn->userPlugInName(); // if (!userFilterName.isEmpty()) // { // kDebug() << "KCMKttsMgr::load: auto-configuring filter " << userFilterName; // bool multiInstance = filterPlugIn->supportsMultiInstance(); // FilterItem fi; // fi.id = filterID; // fi.userFilterName = userFilterName; // fi.plugInName = filterPlugInName; // fi.desktopEntryName = desktopEntryName; // fi.enabled = true; // fi.multiInstance = multiInstance; // // Determine if plugin is an SBD filter. // bool isSbd = filterPlugIn->isSBD(); // if (isSbd) // m_sbdFilterListModel.appendRow(fi); // else // m_filterListModel.appendRow(fi); // KConfigGroup filterConfig(m_config, groupName); // filterPlugIn->save(m_config, groupName); // filterConfig.writeEntry("DesktopEntryName", desktopEntryName); // filterConfig.writeEntry("UserFilterName", userFilterName); // filterConfig.writeEntry("Enabled", isSbd); // filterConfig.writeEntry("MultiInstance", multiInstance); // filterConfig.writeEntry("IsSBD", isSbd); // filterIDsList.append(filterID); // } else m_lastFilterID--; // } else // kDebug() << "KCMKttsMgr::load: Unable to load filter plugin " << filterPlugInName // << " DesktopEntryName " << desktopEntryName << endl; // delete filterPlugIn; // } //} // Rewrite list of FilterIDs in case we added any. QString filterIDs = filterIDsList.join (QLatin1String( "," )); generalConfig.writeEntry ("FilterIDs", filterIDs); m_config->sync(); // Update controls based on new states. updateTalkerButtons(); updateFilterButtons(); m_changed = false; m_suppressConfigChanged = false; } /** * 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". */ void KCMKttsMgr::save() { // kDebug() << "KCMKttsMgr::save: Running"; m_changed = false; // Clean up config. m_config->deleteGroup ("General", 0); // Set the group general for the configuration of kttsd itself (no plug ins) KConfigGroup generalConfig (m_config, "General"); // Uncheck and disable KTTSD checkbox if no Talkers are configured. // Enable checkbox if at least one Talker is configured. bool enableJovieWasToggled = false; //if (m_talkerListModel.rowCount() == 0) //{ //enableKttsdWasToggled = enableKttsdCheckBox->isChecked(); //enableKttsdCheckBox->setChecked(false); //enableKttsdCheckBox->setEnabled(false); // Might as well zero LastTalkerID as well. //m_lastTalkerID = 0; //} //else enableJovieCheckBox->setEnabled (true); generalConfig.writeEntry ("EnableKttsd", enableJovieCheckBox->isChecked()); // Get ordered list of all talker IDs. QList talkers; QStringList talkerIDsList; KConfigGroup talkerGroup (m_config, "Talkers"); talkerGroup.deleteGroup(); for (int i = 0; i < m_talkerListModel.rowCount(); ++i) { TalkerCode talker = m_talkerListModel.getRow (i); talkers << talker; talkerGroup.writeEntry (talker.name(), talker.getTalkerCode()); talkerIDsList << talker.name(); } QString talkerIDs = talkerIDsList.join (QLatin1String( "," )); generalConfig.writeEntry ("TalkerIDs", talkerIDs); // Erase obsolete Talker_nn sections. QStringList groupList = m_config->groupList(); int groupListCount = groupList.count(); for (int groupNdx = 0; groupNdx < groupListCount; ++groupNdx) { QString groupName = groupList[groupNdx]; if (groupName.left (7) == QLatin1String( "Talker_" )) { QString groupTalkerID = groupName.mid (7); if (!talkerIDsList.contains (groupTalkerID)) m_config->deleteGroup (groupName, 0); } } // Get ordered list of all filter IDs. Record enabled state of each filter. QStringList filterIDsList; for (int i = 0; i < m_filterListModel.rowCount(); ++i) { FilterItem fi = m_filterListModel.getRow (i); filterIDsList.append (fi.id); KConfigGroup filterConfig (m_config, QLatin1String( "Filter_" ) + fi.id); filterConfig.writeEntry ("Enabled", fi.enabled); } QString filterIDs = filterIDsList.join (QLatin1String( "," )); generalConfig.writeEntry ("FilterIDs", filterIDs); // Erase obsolete Filter_nn sections. for (int groupNdx = 0; groupNdx < groupListCount; ++groupNdx) { QString groupName = groupList[groupNdx]; if (groupName.left (7) == QLatin1String( "Filter_" )) { QString groupFilterID = groupName.mid (7); if (!filterIDsList.contains (groupFilterID)) m_config->deleteGroup (groupName, 0); } } m_config->sync(); // apply changes in the jobs page if it exists if (m_jobMgrWidget) { m_jobMgrWidget->save(); } // If we automatically unchecked the Enable KTTSD checkbox, stop KTTSD. if (enableJovieWasToggled) slotEnableJovie_toggled (false); else { // If Jovie is running, reinitialize it. if (m_kspeech) { kDebug() << "Restarting Jovie"; m_kspeech->reinit(); } } } void KCMKttsMgr::slotTabChanged() { // TODO: Commentting this out to avoid a crash. It seems there is a bug in // KDialog. The workaround is to call setDefaultButton(), but that method // is not available to a KCModule. Uncomment this when bug is fixed. // setButtons(buttons()); int currentPageIndex = mainTab->currentIndex(); if (currentPageIndex == wpJobs) { if (m_changed) { KMessageBox::information (this, i18n ("You have made changes to the configuration but have not saved them yet. " "Click Apply to save the changes or Cancel to abandon the changes.")); } } } /** * 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. */ void KCMKttsMgr::defaults() { // kDebug() << "Running: KCMKttsMgr::defaults: Running"; int currentPageIndex = mainTab->currentIndex(); // configChanged(); } /** * This is a static method which gets called to realize the modules settings * during the startup of KDE. NOTE that most modules do not implement this * method, but modules like the keyboard and mouse modules, which directly * interact with the X-server, need this method. As this method is static, * it can avoid to create an instance of the user interface, which is often * not needed in this case. */ void KCMKttsMgr::init() { // kDebug() << "KCMKttsMgr::init: Running"; } /** * This function returns the small quickhelp. * That is displayed in the sidebar in the KControl */ QString KCMKttsMgr::quickHelp() const { // kDebug() << "KCMKttsMgr::quickHelp: Running"; return i18n ( "

Text-to-Speech

" "

This is the configuration for the text-to-speech D-Bus service

" "

This allows other applications to access text-to-speech resources

" "

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

"); } const KAboutData* KCMKttsMgr::aboutData() const { KAboutData *about = new KAboutData (I18N_NOOP ("jovie"), 0, ki18n ("KCMKttsMgr"), 0, KLocalizedString(), KAboutData::License_GPL, ki18n ("(c) 2010, Jeremy Whiting")); about->addAuthor (ki18n ("José Pablo Ezequiel Fernández"), ki18n ("Author") , "pupeno@kde.org"); about->addAuthor (ki18n ("Gary Cramblitt"), ki18n ("Maintainer") , "garycramblitt@comcast.net"); about->addAuthor (ki18n ("Olaf Schmidt"), ki18n ("Contributor"), "ojschmidt@kde.org"); about->addAuthor (ki18n ("Paul Giannaros"), ki18n ("Contributor"), "ceruleanblaze@gmail.com"); return about; } /** * Loads the configuration plug in for a named filter plug in. * @param plugInName DesktopEntryName of the plugin. * @return Pointer to the configuration plugin for the Filter. */ KttsFilterConf* KCMKttsMgr::loadFilterPlugin (const QString& plugInName) { // kDebug() << "KCMKttsMgr::loadPlugin: Running"; // Find the plugin. KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" ), QString (QLatin1String( "DesktopEntryName == '%1'" )).arg (plugInName)); if (offers.count() == 1) { // When the entry is found, load the plug in // First create a factory for the library KPluginFactory *factory = KPluginLoader(QLatin1String( offers[0]->library().toLatin1() )).factory(); if (factory) { // If the factory is created successfully, instantiate the KttsFilterConf class for the // specific plug in to get the plug in configuration object. KttsFilterConf *plugIn = factory->create (); if (plugIn) { // If everything went ok, return the plug in pointer. return plugIn; } else { // Something went wrong, returning null. kDebug() << "KCMKttsMgr::loadFilterPlugin: Unable to instantiate KttsFilterConf class for plugin " << plugInName; return NULL; } } else { // Something went wrong, returning null. kDebug() << "KCMKttsMgr::loadFilterPlugin: Unable to create Factory object for plugin " << plugInName; return NULL; } } // The plug in was not found (unexpected behaviour, returns null). kDebug() << "KCMKttsMgr::loadFilterPlugin: KTrader did not return an offer for plugin " << plugInName; return NULL; } /** * Add a talker. */ void KCMKttsMgr::slotAddTalkerButton_clicked() { QPointer dlg = new AddTalker (this); if (dlg->exec() == QDialog::Accepted) { TalkerCode code = dlg->getTalkerCode(); // Add to list of Talkers. m_talkerListModel.appendRow (code); // Make sure visible. const QModelIndex modelIndex = m_talkerListModel.index (m_talkerListModel.rowCount() - 1, 0, QModelIndex()); talkersView->scrollTo (modelIndex); // Select the new item, update buttons. talkersView->setCurrentIndex (modelIndex); updateTalkerButtons(); // Inform Control Center that change has been made. configChanged(); } delete dlg; kDebug() << "KCMKttsMgr::addTalker: done."; } void KCMKttsMgr::slotAddFilterButton_clicked() { addFilter(); } /** * Add a filter. */ void KCMKttsMgr::addFilter() { QTreeView* lView = filtersView; FilterListModel* model = qobject_cast (lView->model()); // Build a list of filters that support multiple instances and let user choose. QStringList filterPlugInNames; for (int i = 0; i < model->rowCount(); ++i) { FilterItem fi = model->getRow (i); if (fi.multiInstance) { if (!filterPlugInNames.contains (fi.plugInName)) filterPlugInNames.append (fi.plugInName); } } // Append those available plugins not yet in the list at all. KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" )); for (int i = 0; i < offers.count() ; ++i) { QString filterPlugInName = offers[i]->name(); if (countFilterPlugins (filterPlugInName) == 0) { QString desktopEntryName = FilterNameToDesktopEntryName (filterPlugInName); KttsFilterConf* filterConf = loadFilterPlugin (desktopEntryName); if (filterConf) { filterPlugInNames.append (filterPlugInName); delete filterConf; } } } // If no choice (shouldn't happen), bail out. // kDebug() << "KCMKttsMgr::addFilter: filterPluginNames = " << filterPlugInNames; if (filterPlugInNames.count() == 0) return; // If exactly one choice, skip selection dialog, otherwise display list to user to select from. bool okChosen = false; QString filterPlugInName; if (filterPlugInNames.count() > 1) { filterPlugInName = KInputDialog::getItem ( i18n ("Select Filter"), i18n ("Filter"), filterPlugInNames, 0, false, &okChosen, this); if (!okChosen) return; } else filterPlugInName = filterPlugInNames[0]; // kDebug() << "KCMKttsMgr::addFilter: filterPlugInName = " << filterPlugInName; // Assign a new Filter ID for the filter. Wraps around to 1. QString filterID = QString::number (m_lastFilterID + 1); // Erase extraneous Filter configuration entries that might be there. m_config->deleteGroup (QLatin1String ("Filter_") + filterID, 0); m_config->sync(); // Get DesktopEntryName from the translated name. QString desktopEntryName = FilterNameToDesktopEntryName (filterPlugInName); // This shouldn't happen, but just in case. if (desktopEntryName.isEmpty()) return; // Load the plugin. m_loadedFilterPlugIn = loadFilterPlugin (desktopEntryName); if (!m_loadedFilterPlugIn) return; // Permit plugin to autoconfigure itself. m_loadedFilterPlugIn->load (m_config, QLatin1String ("Filter_") + filterID); // Display configuration dialog for user to configure the plugin. configureFilter(); // Did user Cancel? if (!m_loadedFilterPlugIn) { delete m_configDlg; m_configDlg = 0; return; } // Get user's name for Filter. QString userFilterName = m_loadedFilterPlugIn->userPlugInName(); // If user properly configured the plugin, save its configuration. if (!userFilterName.isEmpty()) { // Let plugin save its configuration. m_loadedFilterPlugIn->save (m_config, QLatin1String ("Filter_" ) + filterID); // Record last Filter ID used for next add. m_lastFilterID = filterID.toInt(); // Determine if filter supports multiple instances. bool multiInstance = m_loadedFilterPlugIn->supportsMultiInstance(); // Record configuration data. Note, might as well do this now. KConfigGroup filterConfig (m_config, QLatin1String ("Filter_" ) + filterID); filterConfig.writeEntry ("DesktopEntryName", desktopEntryName); filterConfig.writeEntry ("UserFilterName", userFilterName); filterConfig.writeEntry ("MultiInstance", multiInstance); filterConfig.writeEntry ("Enabled", true); m_config->sync(); // Add listview item. FilterItem fi; fi.id = filterID; fi.plugInName = filterPlugInName; fi.userFilterName = userFilterName; fi.desktopEntryName = desktopEntryName; fi.multiInstance = multiInstance; fi.enabled = true; model->appendRow (fi); // Make sure visible. QModelIndex modelIndex = model->index (model->rowCount() - 1, 0, QModelIndex()); lView->scrollTo (modelIndex); // Select the new item, update buttons. lView->setCurrentIndex (modelIndex); updateFilterButtons(); // Inform Control Center that change has been made. configChanged(); } // Don't need plugin in memory anymore. delete m_loadedFilterPlugIn; m_loadedFilterPlugIn = 0; delete m_configDlg; m_configDlg = 0; // kDebug() << "KCMKttsMgr::addFilter: done."; } /** * Remove talker. */ void KCMKttsMgr::slotRemoveTalkerButton_clicked() { // kDebug() << "KCMKttsMgr::removeTalker: Running"; // Get the selected talker. QModelIndex modelIndex = talkersView->currentIndex(); if (!modelIndex.isValid()) return; // Delete the talker from configuration file? QString talkerID = m_talkerListModel.getRow (modelIndex.row()).name(); m_config->deleteGroup (QLatin1String ("Talker_") + talkerID, 0); // Delete the talker from the list of Talkers. m_talkerListModel.removeRow (modelIndex.row()); updateTalkerButtons(); // Emit configuration changed. configChanged(); } void KCMKttsMgr::slotRemoveFilterButton_clicked() { removeFilter(); } /** * Remove filter. */ void KCMKttsMgr::removeFilter() { // kDebug() << "KCMKttsMgr::removeFilter: Running"; FilterListModel* model; QTreeView* lView = filtersView; model = qobject_cast (lView->model()); QModelIndex modelIndex = lView->currentIndex(); if (!modelIndex.isValid()) return; QString filterID = model->getRow (modelIndex.row()).id; // Delete the filter from list view. model->removeRow (modelIndex.row()); updateFilterButtons(); // Delete the filter from the configuration file? kDebug() << "KCMKttsMgr::removeFilter: removing FilterID = " << filterID << " from config file."; m_config->deleteGroup (QLatin1String ("Filter_") + filterID, 0); // Emit configuration changed. configChanged(); } void KCMKttsMgr::slotHigherTalkerPriorityButton_clicked() { QModelIndex modelIndex = talkersView->currentIndex(); if (!modelIndex.isValid()) return; m_talkerListModel.swap (modelIndex.row(), modelIndex.row() - 1); modelIndex = m_talkerListModel.index (modelIndex.row() - 1, 0, QModelIndex()); talkersView->scrollTo (modelIndex); talkersView->setCurrentIndex (modelIndex); updateTalkerButtons(); configChanged(); } void KCMKttsMgr::slotHigherFilterPriorityButton_clicked() { QModelIndex modelIndex = filtersView->currentIndex(); if (!modelIndex.isValid()) return; m_filterListModel.swap (modelIndex.row(), modelIndex.row() - 1); modelIndex = m_filterListModel.index (modelIndex.row() - 1, 0, QModelIndex()); filtersView->scrollTo (modelIndex); filtersView->setCurrentIndex (modelIndex); updateFilterButtons(); configChanged(); } void KCMKttsMgr::slotLowerTalkerPriorityButton_clicked() { QModelIndex modelIndex = talkersView->currentIndex(); if (!modelIndex.isValid()) return; m_talkerListModel.swap (modelIndex.row(), modelIndex.row() + 1); modelIndex = m_talkerListModel.index (modelIndex.row() + 1, 0, QModelIndex()); talkersView->scrollTo (modelIndex); talkersView->setCurrentIndex (modelIndex); updateTalkerButtons(); configChanged(); } void KCMKttsMgr::slotLowerFilterPriorityButton_clicked() { QModelIndex modelIndex = filtersView->currentIndex(); if (!modelIndex.isValid()) return; m_filterListModel.swap (modelIndex.row(), modelIndex.row() + 1); modelIndex = m_filterListModel.index (modelIndex.row() + 1, 0, QModelIndex()); filtersView->scrollTo (modelIndex); filtersView->setCurrentIndex (modelIndex); updateFilterButtons(); configChanged(); } /** * Update the status of the Talker buttons. */ void KCMKttsMgr::updateTalkerButtons() { // kDebug() << "KCMKttsMgr::updateTalkerButtons: Running"; QModelIndex modelIndex = talkersView->currentIndex(); if (modelIndex.isValid()) { removeTalkerButton->setEnabled (true); configureTalkerButton->setEnabled (true); higherTalkerPriorityButton->setEnabled (modelIndex.row() != 0); lowerTalkerPriorityButton->setEnabled (modelIndex.row() < (m_talkerListModel.rowCount() - 1)); } else { removeTalkerButton->setEnabled (false); configureTalkerButton->setEnabled (false); higherTalkerPriorityButton->setEnabled (false); lowerTalkerPriorityButton->setEnabled (false); } // kDebug() << "KCMKttsMgr::updateTalkerButtons: Exiting"; } /** * Update the status of the normal Filter buttons. */ void KCMKttsMgr::updateFilterButtons() { // kDebug() << "KCMKttsMgr::updateFilterButtons: Running"; QModelIndex modelIndex = filtersView->currentIndex(); if (modelIndex.isValid()) { removeFilterButton->setEnabled (true); configureFilterButton->setEnabled (true); higherFilterPriorityButton->setEnabled (modelIndex.row() != 0); lowerFilterPriorityButton->setEnabled (modelIndex.row() < (m_filterListModel.rowCount() - 1)); } else { removeFilterButton->setEnabled (false); configureFilterButton->setEnabled (false); higherFilterPriorityButton->setEnabled (false); lowerFilterPriorityButton->setEnabled (false); } // kDebug() << "KCMKttsMgr::updateFilterButtons: Exiting"; } /** * This signal is emitted whenever user checks/unchecks the Enable TTS System check box. */ void KCMKttsMgr::slotEnableJovie_toggled (bool) { // Prevent re-entrancy. static bool reenter; if (reenter) return; reenter = true; // See if Jovie is running. bool kttsdRunning = (QDBusConnection::sessionBus().interface()->isServiceRegistered (QLatin1String( "org.kde.jovie" ))); // kDebug() << "KCMKttsMgr::slotEnableKttsd_toggled: kttsdRunning = " << kttsdRunning; // If Enable Jovie check box is checked and it is not running, then start Jovie. if (enableJovieCheckBox->isChecked()) { if (!kttsdRunning) { // kDebug() << "KCMKttsMgr::slotEnableKttsd_toggled:: Starting Jovie"; QString error; if (KToolInvocation::startServiceByDesktopName (QLatin1String( "jovie" ), QStringList(), &error)) { kDebug() << "Starting Jovie failed with message " << error; enableJovieCheckBox->setChecked (false); } else { configChanged(); jovieStarted(); } } } else // If check box is not checked and it is running, then stop Jovie. { if (kttsdRunning) { // kDebug() << "KCMKttsMgr::slotEnableKttsd_toggled:: Stopping Jovie"; if (!m_kspeech) m_kspeech = new OrgKdeKSpeechInterface (QLatin1String( "org.kde.jovie" ), QLatin1String( "/KSpeech" ), QDBusConnection::sessionBus()); m_kspeech->kttsdExit(); delete m_kspeech; m_kspeech = 0; configChanged(); } } reenter = false; } /** * This slot is called whenever KTTSD starts or restarts. */ void KCMKttsMgr::jovieStarted() { // kDebug() << "KCMKttsMgr::jovieStarted: Running"; bool kttsdLoaded = (m_jobMgrWidget != 0); // Load Job Manager Part library. if (!kttsdLoaded) { m_jobMgrWidget = new KttsJobMgr (this); if (m_jobMgrWidget) { connect (m_jobMgrWidget, SIGNAL (configChanged()), this, SLOT (configChanged())); // Add the Job Manager part as a new tab. mainTab->addTab (m_jobMgrWidget, i18n ("Jobs")); kttsdLoaded = true; } else kDebug() << "KCMKttsMgr::jovieStarted: Could not create kttsjobmgr part."; } // Check/Uncheck the Enable KTTSD check box. if (kttsdLoaded) { enableJovieCheckBox->setChecked (true); m_kspeech = new OrgKdeKSpeechInterface (QLatin1String( "org.kde.jovie" ), QLatin1String( "/KSpeech" ), QDBusConnection::sessionBus()); m_kspeech->setParent (this); m_kspeech->setApplicationName (QLatin1String( "KCMKttsMgr" )); m_kspeech->setIsSystemManager (true); // Connect KTTSD DBUS signals to our slots. connect (m_kspeech, SIGNAL (kttsdStarted()), this, SLOT (jovieStarted())); connect (m_kspeech, SIGNAL (kttsdExiting()), this, SLOT (jovieExiting())); connect (QDBusConnection::sessionBus().interface(), SIGNAL (serviceUnregistered(QString)), this, SLOT (slotServiceUnregistered(QString))); connect (QDBusConnection::sessionBus().interface(), SIGNAL (serviceOwnerChanged(QString,QString,QString)), this, SLOT (slotServiceOwnerChanged(QString,QString,QString))); kttsdVersion->setText (i18n ("Jovie Version: %1", m_kspeech->version())); } else { enableJovieCheckBox->setChecked (false); delete m_kspeech; m_kspeech = 0; } } void KCMKttsMgr::slotServiceUnregistered (const QString &service) { if (service == QLatin1String ("org.kde.jovie")) { jovieExiting(); } } void KCMKttsMgr::slotServiceOwnerChanged (const QString &service, const QString &, const QString &newOwner) { if (service == QLatin1String ("org.kde.jovie") && newOwner.isEmpty()) { jovieExiting(); } } /** * This slot is called whenever KTTSD is about to exit. */ void KCMKttsMgr::jovieExiting() { // kDebug() << "KCMKttsMgr::kttsdExiting: Running"; if (m_jobMgrWidget) { mainTab->removeTab (wpJobs); delete m_jobMgrWidget; m_jobMgrWidget = 0; } enableJovieCheckBox->setChecked (false); disconnect (QDBusConnection::sessionBus().interface(), 0, this, 0); delete m_kspeech; m_kspeech = 0; kttsdVersion->setText (i18n ("Jovie not running")); } /** * User has requested display of talker configuration dialog. */ void KCMKttsMgr::slotConfigureTalkerButton_clicked() { // Get highlighted Talker from ListView. QModelIndex modelIndex = talkersView->currentIndex(); if (!modelIndex.isValid()) return; TalkerCode tc = m_talkerListModel.getRow(modelIndex.row()); QPointer dlg = new AddTalker (this); dlg->setTalkerCode(tc); if (dlg->exec() == QDialog::Accepted) { TalkerCode code = dlg->getTalkerCode(); // Change the existing talker code to this new code. m_talkerListModel.updateRow(modelIndex.row(), code); configChanged(); } } void KCMKttsMgr::slotConfigureFilterButton_clicked() { configureFilterItem(); } /** * User has requested display of filter configuration dialog. */ void KCMKttsMgr::configureFilterItem() { // Get highlighted plugin from Filter ListView and load into memory. QTreeView* lView; FilterListModel* model; lView = filtersView; model = &m_filterListModel; QModelIndex modelIndex = lView->currentIndex(); if (!modelIndex.isValid()) return; FilterItem fi = model->getRow (modelIndex.row()); QString filterID = fi.id; QString filterPlugInName = fi.plugInName; QString desktopEntryName = fi.desktopEntryName; if (desktopEntryName.isEmpty()) return; m_loadedFilterPlugIn = loadFilterPlugin (desktopEntryName); if (!m_loadedFilterPlugIn) return; // kDebug() << "KCMKttsMgr::slot_configureFilter: plugin for " << filterPlugInName << " loaded successfully."; // Tell plugin to load its configuration. // kDebug() << "KCMKttsMgr::slot_configureFilter: about to call plugin load() method with Filter ID = " << filterID; m_loadedFilterPlugIn->load (m_config, QLatin1String ("Filter_") + filterID); // Display configuration dialog. configureFilter(); // Did user Cancel? if (!m_loadedFilterPlugIn) { delete m_configDlg; m_configDlg = 0; return; } // Get user's name for the plugin. QString userFilterName = m_loadedFilterPlugIn->userPlugInName(); // If user properly configured the plugin, save the configuration. if (!userFilterName.isEmpty()) { // Let plugin save its configuration. m_loadedFilterPlugIn->save (m_config, QLatin1String ("Filter_") + filterID); // Save configuration. KConfigGroup filterConfig (m_config, QLatin1String ("Filter_") + filterID); filterConfig.writeEntry ("DesktopEntryName", desktopEntryName); filterConfig.writeEntry ("UserFilterName", userFilterName); filterConfig.writeEntry ("Enabled", true); filterConfig.writeEntry ("MultiInstance", m_loadedFilterPlugIn->supportsMultiInstance()); m_config->sync(); // Update display. FilterItem fi; fi.id = filterID; fi.desktopEntryName = desktopEntryName; fi.userFilterName = userFilterName; fi.enabled = true; fi.multiInstance = m_loadedFilterPlugIn->supportsMultiInstance(); model->updateRow (modelIndex.row(), fi); // Inform Control Center that configuration has changed. configChanged(); } delete m_configDlg; m_configDlg = 0; } /** * Display filter configuration dialog. The plugin is assumed already loaded into * memory referenced by m_loadedFilterPlugIn. */ void KCMKttsMgr::configureFilter() { if (!m_loadedFilterPlugIn) return; m_configDlg = new KDialog (this); m_configDlg->setCaption (i18n ("Filter Configuration")); m_configDlg->setButtons (KDialog::Help | KDialog::Default | KDialog::Ok | KDialog::Cancel); m_configDlg->setDefaultButton (KDialog::Cancel); m_loadedFilterPlugIn->setMinimumSize (m_loadedFilterPlugIn->minimumSizeHint()); m_loadedFilterPlugIn->show(); m_configDlg->setMainWidget (m_loadedFilterPlugIn); m_configDlg->setHelp (QLatin1String( "configure-filter" ), QLatin1String( "jovie" )); m_configDlg->enableButtonOk (false); connect (m_loadedFilterPlugIn, SIGNAL (changed(bool)), this, SLOT (slotConfigFilterDlg_ConfigChanged())); connect (m_configDlg, SIGNAL (defaultClicked()), this, SLOT (slotConfigFilterDlg_DefaultClicked())); connect (m_configDlg, SIGNAL (cancelClicked()), this, SLOT (slotConfigFilterDlg_CancelClicked())); // Display the dialog. m_configDlg->exec(); } /** * Count number of configured Filters with the specified plugin name. */ int KCMKttsMgr::countFilterPlugins (const QString& filterPlugInName) { int cnt = 0; for (int i = 0; i < m_filterListModel.rowCount(); ++i) { FilterItem fi = m_filterListModel.getRow (i); if (fi.plugInName == filterPlugInName) ++cnt; } return cnt; } void KCMKttsMgr::slotConfigTalkerDlg_ConfigChanged() { //m_configDlg->enableButtonOk(!m_loadedTalkerPlugIn->getTalkerCode().isEmpty()); } void KCMKttsMgr::slotConfigFilterDlg_ConfigChanged() { m_configDlg->enableButtonOk (!m_loadedFilterPlugIn->userPlugInName().isEmpty()); } void KCMKttsMgr::slotConfigTalkerDlg_DefaultClicked() { //m_loadedTalkerPlugIn->defaults(); } void KCMKttsMgr::slotConfigFilterDlg_DefaultClicked() { m_loadedFilterPlugIn->defaults(); } void KCMKttsMgr::slotConfigTalkerDlg_CancelClicked() { //delete m_loadedTalkerPlugIn; //m_loadedTalkerPlugIn = 0; } void KCMKttsMgr::slotConfigFilterDlg_CancelClicked() { delete m_loadedFilterPlugIn; m_loadedFilterPlugIn = 0; } /** * Uses KTrader to convert a translated Filter Plugin Name to DesktopEntryName. * @param name The translated plugin name. From Name= line in .desktop file. * @return DesktopEntryName. The name of the .desktop file (less .desktop). * QString() if not found. */ QString KCMKttsMgr::FilterNameToDesktopEntryName (const QString& name) { if (name.isEmpty()) return QString(); const KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" )); for (int ndx = 0; ndx < offers.count(); ++ndx) if (offers[ndx]->name() == name) return offers[ndx]->desktopEntryName(); return QString(); } /** * Uses KTrader to convert a DesktopEntryName into a translated Filter Plugin Name. * @param desktopEntryName The DesktopEntryName. * @return The translated Name of the plugin, from Name= line in .desktop file. */ QString KCMKttsMgr::FilterDesktopEntryNameToName (const QString& desktopEntryName) { if (desktopEntryName.isEmpty()) return QString(); KService::List offers = KServiceTypeTrader::self()->query (QLatin1String( "Jovie/FilterPlugin" ), QString (QLatin1String( "DesktopEntryName == '%1'" )).arg (desktopEntryName)); if (offers.count() == 1) return offers[0]->name(); else return QString(); } void KCMKttsMgr::slotFilterListView_clicked (const QModelIndex & index) { if (!index.isValid()) return; if (index.column() != 0) return; if (index.row() < 0 || index.row() >= m_filterListModel.rowCount()) return; FilterItem fi = m_filterListModel.getRow (index.row()); fi.enabled = !fi.enabled; m_filterListModel.updateRow (index.row(), fi); configChanged(); }