SVN_SILENT made messages (.desktop file)
[kdeaccessibility.git] / kttsd / kttsjobmgr / kttsjobmgr.cpp
blob295435d4231c5e22bfe83f57f9c16c4904d87185
1 /***************************************************** vim:set ts=4 sw=4 sts=4:
2 A KPart to display running jobs in KTTSD and permit user to stop, rewind,
3 advance, change Talker, etc.
4 -------------------
5 Copyright : (C) 2004,2005 by Gary Cramblitt <garycramblitt@comcast.net>
6 Copyright : (C) 2009 by Jeremy Whiting <jeremy@scitools.com>
7 -------------------
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 ******************************************************************************/
24 // KttsJobMgr includes.
25 #include "kttsjobmgr.h"
26 #include "kttsjobmgr.moc"
27 #include "ui_kttsjobmgr.h"
29 // QT includes.
30 #include <QtCore/QObject>
31 #include <QtGui/QLabel>
32 #include <QtGui/QSplitter>
33 #include <QtGui/QClipboard>
34 #include <QtGui/QPushButton>
35 #include <QtCore/QList>
36 #include <QtGui/QTreeView>
37 #include <QtCore/QMimeData>
38 #include <QtDBus/QtDBus>
40 // KDE includes.
41 #include <kcomponentdata.h>
42 #include <klocale.h>
43 #include <kaboutdata.h>
44 #include <kicon.h>
45 #include <kdebug.h>
46 #include <kencodingfiledialog.h>
47 #include <kinputdialog.h>
48 #include <ktextedit.h>
49 #include <kvbox.h>
50 #include <kdemacros.h>
51 #include <kparts/genericfactory.h>
52 #include <kspeech.h>
54 // KTTS includes.
55 #include "talkercode.h"
56 #include "selecttalkerdlg.h"
57 #include "jobinfolistmodel.h"
59 typedef KParts::GenericFactory<KttsJobMgrPart> KttsJobMgrPartFactory;
60 K_EXPORT_COMPONENT_FACTORY( libkttsjobmgrpart, KttsJobMgrPartFactory )
62 KAboutData* KttsJobMgrPart::createAboutData()
64 KAboutData *about = new KAboutData("kttsjobmgr", 0, ki18n("KttsJobMgr"), "1.99");
65 return about;
68 KttsJobMgrPart::KttsJobMgrPart(QWidget *parentWidget, QObject *parent, const QStringList& args) :
69 KParts::ReadOnlyPart(parent)
70 // m_kspeech(QDBus::sessionBus().findInterface<org::kde::KSpeech>("org.kde.kttsd", "/KSpeech"))
72 m_ui = new Ui::kttsjobmgr;
73 QWidget * widget = new QWidget(parentWidget);
74 m_ui->setupUi(widget);
76 //DBusAbstractInterfacePrivate
77 Q_UNUSED(args);
78 m_kspeech = new OrgKdeKSpeechInterface("org.kde.kttsd", "/KSpeech", QDBusConnection::sessionBus());
79 m_kspeech->setParent(this);
81 // Establish ourself as a System Manager.
82 m_kspeech->setApplicationName("KCMKttsMgr");
83 m_kspeech->setIsSystemManager(true);
85 // Initialize some variables.
86 m_selectOnTextSet = false;
88 // All the ktts components use the same catalog.
89 KGlobal::locale()->insertCatalog("kttsd");
91 m_jobListModel = new JobInfoListModel();
92 m_ui->m_jobListView->setModel(m_jobListModel);
94 // TODO: Do not sort the list.
95 // m_jobListView->setSorting(-1);
97 m_jobButtons << m_ui->job_hold;
98 m_jobButtons << m_ui->job_resume;
99 m_jobButtons << m_ui->job_restart;
100 m_jobButtons << m_ui->job_remove;
101 m_jobButtons << m_ui->job_removeall;
102 m_jobButtons << m_ui->job_later;
103 m_jobButtons << m_ui->job_prevsentence;
104 m_jobButtons << m_ui->job_nextsentence;
105 m_jobButtons << m_ui->job_changetalker;
107 m_ui->job_hold->setIcon(KIcon("process-stop"));
108 connect (m_ui->job_hold, SIGNAL(clicked()), this, SLOT(slot_job_hold()));
109 m_ui->job_resume->setIcon(KIcon("system-run"));
110 connect (m_ui->job_resume, SIGNAL(clicked()), this, SLOT(slot_job_resume()));
111 m_ui->job_restart->setIcon(KIcon("edit-redo"));
112 connect (m_ui->job_restart, SIGNAL(clicked()), this, SLOT(slot_job_restart()));
113 m_ui->job_remove->setIcon(KIcon("user-trash"));
114 connect (m_ui->job_remove, SIGNAL(clicked()), this, SLOT(slot_job_remove()));
115 m_ui->job_removeall->setIcon(KIcon("user-trash"));
116 connect (m_ui->job_removeall, SIGNAL(clicked()), this, SLOT(slot_job_remove_all()));
117 m_ui->job_later->setIcon(KIcon("go-down"));
118 connect (m_ui->job_later, SIGNAL(clicked()), this, SLOT(slot_job_move()));
120 m_ui->job_prevsentence->setIcon(KIcon("arrow-left"));
121 connect (m_ui->job_prevsentence, SIGNAL(clicked()), this, SLOT(slot_job_prev_sen()));
122 m_ui->job_nextsentence->setIcon(KIcon("arrow-right"));
123 connect (m_ui->job_nextsentence, SIGNAL(clicked()), this, SLOT(slot_job_next_sen()));
125 m_ui->speak_clipboard->setIcon(KIcon("klipper"));
126 connect (m_ui->speak_clipboard, SIGNAL(clicked()), this, SLOT(slot_speak_clipboard()));
127 m_ui->speak_file->setIcon(KIcon("document-open"));
128 connect (m_ui->speak_file, SIGNAL(clicked()), this, SLOT(slot_speak_file()));
129 m_ui->job_changetalker->setIcon(KIcon("translate"));
130 connect (m_ui->job_changetalker, SIGNAL(clicked()), this, SLOT(slot_job_change_talker()));
131 m_ui->refresh->setIcon(KIcon("view-refresh"));
132 connect (m_ui->refresh, SIGNAL(clicked()), this, SLOT(slot_refresh()));
134 // Disable job buttons until a job is selected.
135 enableJobActions(false);
137 // Set the main widget for the part.
138 setWidget(widget);
140 connect(m_ui->m_jobListView, SIGNAL(clicked(const QModelIndex&)),
141 this, SLOT(slot_jobListView_clicked()));
143 // Fill the Job List.
144 refreshJobList();
146 // Select first item (if any).
147 autoSelectInJobListView();
149 // Connect DBUS Signals emitted by KTTSD to our own slots.
150 connect(m_kspeech, SIGNAL(kttsdStarted()),
151 this, SLOT(kttsdStarted()));
152 connect(m_kspeech, SIGNAL(jobStateChanged(const QString&, int, int)),
153 this, SLOT(jobStateChanged(const QString&, int, int)));
154 connect(m_kspeech, SIGNAL(marker(const QString&, int, int, const QString&)),
155 this, SLOT(marker(const QString&, int, int, const QString&)));
157 m_extension = new KttsJobMgrBrowserExtension(this);
159 // Divide splitter in half. ListView gets half. Buttons and Current Sentence get half.
160 int halfSplitterSize = m_ui->splitter->height()/2;
161 QList<int> splitterSizes;
162 splitterSizes.append(halfSplitterSize);
163 splitterSizes.append(halfSplitterSize);
164 m_ui->splitter->setSizes(splitterSizes);
167 KttsJobMgrPart::~KttsJobMgrPart()
169 KGlobal::locale()->removeCatalog("kttsd");
170 closeUrl();
171 delete m_jobListModel;
174 bool KttsJobMgrPart::openFile()
176 return true;
179 bool KttsJobMgrPart::closeUrl()
181 return true;
185 * This slot is connected to the Job List View clicked signal.
187 void KttsJobMgrPart::slot_jobListView_clicked()
189 // Enable job buttons.
190 enableJobActions(true);
194 * Slots connected to buttons.
196 void KttsJobMgrPart::slot_job_hold()
198 m_kspeech->pause();
201 void KttsJobMgrPart::slot_job_resume()
203 m_kspeech->resume();
206 void KttsJobMgrPart::slot_job_restart()
208 int jobNum = getCurrentJobNum();
209 // kDebug() << "KttsJobMgrPart::slot_job_restart: jobNum = " << jobNum;
210 if (jobNum)
212 int seq = m_kspeech->moveRelSentence(jobNum, 0);
213 m_kspeech->moveRelSentence(jobNum, -seq);
214 refreshJob(jobNum);
218 void KttsJobMgrPart::slot_job_prev_sen()
220 int jobNum = getCurrentJobNum();
221 if (jobNum)
223 m_kspeech->moveRelSentence(jobNum, -1);
224 refreshJob(jobNum);
228 void KttsJobMgrPart::slot_job_next_sen()
230 int jobNum = getCurrentJobNum();
231 if (jobNum)
233 m_kspeech->moveRelSentence(jobNum, 1);
234 refreshJob(jobNum);
238 void KttsJobMgrPart::slot_job_remove()
240 int jobNum = getCurrentJobNum();
241 if (jobNum)
243 m_kspeech->removeJob(jobNum);
244 m_ui->m_currentSentence->clear();
248 void KttsJobMgrPart::slot_job_remove_all()
250 m_kspeech->removeAllJobs();
251 m_ui->m_currentSentence->clear();
254 void KttsJobMgrPart::slot_job_move()
256 int jobNum = getCurrentJobNum();
257 if (jobNum)
259 m_kspeech->moveJobLater(jobNum);
260 refreshJobList();
261 // Select the job we just moved.
262 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
263 if (index.isValid())
265 m_ui->m_jobListView->setCurrentIndex(index);
266 slot_jobListView_clicked();
271 void KttsJobMgrPart::slot_job_change_talker()
273 QModelIndex index = m_ui->m_jobListView->currentIndex();
274 if (index.isValid())
276 JobInfo job = m_jobListModel->getRow(index.row());
277 QString talkerID = job.talkerID;
278 QStringList talkerIDs = m_talkerCodesToTalkerIDs.values();
279 int ndx = talkerIDs.indexOf(talkerID);
280 QString talkerCode;
281 if (ndx >= 0) talkerCode = m_talkerCodesToTalkerIDs.keys()[ndx];
282 SelectTalkerDlg dlg(widget(), "selecttalkerdialog", i18n("Select Talker"), talkerCode, true);
283 int dlgResult = dlg.exec();
284 if (dlgResult != KDialog::Accepted) return;
285 talkerCode = dlg.getSelectedTalkerCode();
286 int jobNum = job.jobNum;
287 m_kspeech->changeJobTalker(jobNum, talkerCode);
288 refreshJob(jobNum);
292 void KttsJobMgrPart::slot_speak_clipboard()
294 // kDebug() << "KttsJobMgrPart::slot_speak_clipboard: running";
296 // Get the clipboard object.
297 QClipboard *cb = QApplication::clipboard();
299 // Copy text from the clipboard.
300 QString text;
301 KSpeech::SayOptions sayOptions = KSpeech::soNone;
302 const QMimeData* data = cb->mimeData();
303 if (data)
305 if (data->hasFormat("text/html"))
307 // if (m_kspeech->supportsMarkup(NULL, KSpeech::mtHtml))
308 text = data->html();
309 sayOptions = KSpeech::soHtml;
311 if (data->hasFormat("text/ssml"))
313 // if (m_kspeech->supportsMarkup(NULL, KSpeech::mtSsml))
315 QByteArray d = data->data("text/ssml");
316 text = QString(d);
317 sayOptions = KSpeech::soSsml;
321 if (text.isEmpty()) {
322 text = cb->text();
323 sayOptions = KSpeech::soPlainText;
326 // Speak it.
327 if ( !text.isEmpty() )
329 m_kspeech->say(text, sayOptions);
330 // int jobNum = m_kspeech->say(text, sayOptions);
331 // kDebug() << "KttsJobMgrPart::slot_speak_clipboard: started jobNum " << jobNum;
332 // Set flag so that the job we just created will be selected when textSet signal is received.
333 m_selectOnTextSet = true;
337 void KttsJobMgrPart::slot_speak_file()
339 KEncodingFileDialog dlg;
340 KEncodingFileDialog::Result result = dlg.getOpenFileNameAndEncoding();
341 if (result.fileNames.count() == 1)
343 // kDebug() << "KttsJobMgr::slot_speak_file: calling setFile with filename " <<
344 // result.fileNames[0] << " and encoding " << result.encoding << endl;
345 m_kspeech->sayFile(result.fileNames[0], result.encoding);
349 void KttsJobMgrPart::slot_refresh()
351 // Clear TalkerID cache.
352 m_talkerCodesToTalkerIDs.clear();
353 // Get current job number.
354 int jobNum = getCurrentJobNum();
355 refreshJobList();
356 // Select the previously-selected job.
357 if (jobNum)
359 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
360 if (index.isValid())
362 m_ui->m_jobListView->setCurrentIndex(index);
363 slot_jobListView_clicked();
370 * Get the Job Number of the currently-selected job in the Job List View.
371 * @return Job Number of currently-selected job.
372 * 0 if no currently-selected job.
374 int KttsJobMgrPart::getCurrentJobNum()
376 int jobNum = 0;
377 QModelIndex index = m_ui->m_jobListView->currentIndex();
378 if (index.isValid())
379 jobNum = m_jobListModel->getRow(index.row()).jobNum;
380 return jobNum;
384 * Retrieves JobInfo from KTTSD, creates and fills JobInfo object.
385 * @param jobNum Job Number.
387 JobInfo* KttsJobMgrPart::retrieveJobInfo(int jobNum)
389 QByteArray jobInfo = m_kspeech->getJobInfo(jobNum);
390 if (jobInfo != QByteArray()) {
391 JobInfo* job = new JobInfo();
392 QDataStream stream(&jobInfo, QIODevice::ReadOnly);
393 qint32 priority;
394 qint32 state;
395 QString talkerCode;
396 qint32 sentenceNum;
397 qint32 sentenceCount;
398 stream >> priority;
399 stream >> state;
400 stream >> job->appId;
401 stream >> talkerCode;
402 stream >> sentenceNum;
403 stream >> sentenceCount;
404 stream >> job->applicationName;
405 job->jobNum = jobNum;
406 job->priority = priority;
407 job->state = state;
408 job->sentenceNum = sentenceNum;
409 job->sentenceCount = sentenceCount;
410 job->talkerID = cachedTalkerCodeToTalkerID(talkerCode);
411 return job;
413 return NULL;
417 * Refresh display of a single job in the JobListView.
418 * @param jobNum Job Number.
420 void KttsJobMgrPart::refreshJob(int jobNum)
422 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
423 if (index.isValid()) {
424 JobInfo* job = retrieveJobInfo(jobNum);
425 if (job)
426 m_jobListModel->updateRow(index.row(), *job);
427 else
428 m_jobListModel->removeRow(index.row());
433 * Fill the Job List View.
435 void KttsJobMgrPart::refreshJobList()
437 // kDebug() << "KttsJobMgrPart::refreshJobList: Running";
438 m_jobListModel->clear();
439 JobInfoList jobInfoList;
440 enableJobActions(false);
441 QStringList jobNums = m_kspeech->getJobNumbers(KSpeech::jpAll);
442 for (int ndx = 0; ndx < jobNums.count(); ++ndx)
444 QString jobNumStr = jobNums[ndx];
445 kDebug() << "jobNumStr = " << jobNumStr;
446 int jobNum = jobNumStr.toInt(0, 10);
447 kDebug() << "jobNum = " << jobNum;
448 JobInfo* job = retrieveJobInfo(jobNum);
449 if (job)
450 jobInfoList.append(*job);
452 m_jobListModel->setDatastore(jobInfoList);
456 * If nothing selected in Job List View and list not empty, select top item.
457 * If nothing selected and list is empty, disable job buttons.
459 void KttsJobMgrPart::autoSelectInJobListView()
461 // If something selected, nothing to do.
462 if (m_ui->m_jobListView->currentIndex().isValid()) return;
463 // If empty, disable job buttons.
465 if (m_jobListModel->rowCount() == 0)
466 enableJobActions(false);
467 else
469 // Select first item.
470 m_ui->m_jobListView->setCurrentIndex(m_jobListModel->index(0, 0));
471 slot_jobListView_clicked();
476 * Return the Talker ID corresponding to a Talker Code, retrieving from cached list if present.
477 * @param talkerCode Talker Code.
478 * @return Talker ID.
480 QString KttsJobMgrPart::cachedTalkerCodeToTalkerID(const QString& talkerCode)
482 // If in the cache, return that.
483 if (m_talkerCodesToTalkerIDs.contains(talkerCode))
484 return m_talkerCodesToTalkerIDs[talkerCode];
485 else
487 // Otherwise, retrieve Talker ID from KTTSD and cache it.
488 QString talkerID = m_kspeech->talkerToTalkerId(talkerCode);
489 m_talkerCodesToTalkerIDs[talkerCode] = talkerID;
490 // kDebug() << "KttsJobMgrPart::cachedTalkerCodeToTalkerID: talkerCode = " << talkerCode << " talkerID = " << talkerID;
491 return talkerID;
496 * Enables or disables all the job-related buttons.
497 * @param enable True to enable the job-related butons. False to disable.
499 void KttsJobMgrPart::enableJobActions(bool enable)
501 QListIterator<KPushButton *> i(m_jobButtons);
503 while (i.hasNext())
504 (i.next())->setEnabled( enable );
506 if (enable)
508 // Later button only enables if currently selected list item is not bottom of list.
509 QModelIndex index = m_ui->m_jobListView->currentIndex();
510 if (index.isValid())
512 bool enableLater = (index.row() < m_jobListModel->rowCount());
513 m_ui->job_later->setEnabled(enableLater);
518 /** Slots connected to DBUS Signals emitted by KTTSD. */
521 * This signal is emitted when KTTSD starts or restarts after a call to reinit.
523 Q_SCRIPTABLE void KttsJobMgrPart::kttsdStarted() { slot_refresh(); }
527 * This signal is emitted each time the state of a job changes.
528 * @param appId The DBUS sender ID of the application that
529 * submitted the job.
530 * @param jobNum Job Number.
531 * @param state Job state. @see KSpeech::JobState.
533 Q_SCRIPTABLE void KttsJobMgrPart::jobStateChanged(const QString &appId, int jobNum, int state)
535 Q_UNUSED(appId);
536 switch (state)
538 case KSpeech::jsQueued:
540 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
541 if (index.isValid())
542 refreshJob(jobNum);
543 else
545 JobInfo* job = retrieveJobInfo(jobNum);
546 if (job) {
547 m_jobListModel->appendRow(*job);
548 // Should we select this job?
549 if (m_selectOnTextSet)
551 m_ui->m_jobListView->setCurrentIndex(m_jobListModel->jobNumToIndex(jobNum));
552 m_selectOnTextSet = false;
553 slot_jobListView_clicked();
557 // If a job not already selected, select this one.
558 autoSelectInJobListView();
559 break;
561 case KSpeech::jsSpeakable:
563 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
564 if (index.isValid()) {
565 JobInfo* job = retrieveJobInfo(jobNum);
566 if (job)
567 m_jobListModel->updateRow(index.row(), *job);
568 else
569 m_jobListModel->removeRow(index.row());
571 break;
573 case KSpeech::jsFiltering:
574 case KSpeech::jsSpeaking:
575 case KSpeech::jsPaused:
576 case KSpeech::jsInterrupted:
577 case KSpeech::jsFinished:
579 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
580 if (index.isValid())
582 JobInfo job = m_jobListModel->getRow(index.row());
583 job.state = state;
584 m_jobListModel->updateRow(index.row(), job);
586 m_ui->m_currentSentence->setPlainText(QString());
587 break;
589 case KSpeech::jsDeleted:
591 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
592 if (index.isValid())
593 m_jobListModel->removeRow(index.row());
594 autoSelectInJobListView();
595 break;
601 * This signal is emitted when a marker is processed.
602 * Currently only emits mtSentenceBegin and mtSentenceEnd.
603 * @param appId The DBUS sender ID of the application that submitted the job.
604 * @param jobNum Job Number of the job emitting the marker.
605 * @param markerType The type of marker.
606 * Currently either mtSentenceBegin or mtSentenceEnd.
607 * @param markerData Data for the marker.
608 * Currently, this is the sequence number of the sentence
609 * begun or ended. Sequence numbers begin at 1.
611 Q_SCRIPTABLE void KttsJobMgrPart::marker(const QString &appId, int jobNum, int markerType, const QString &markerData)
613 Q_UNUSED(appId);
614 if (KSpeech::mtSentenceBegin == markerType) {
615 QModelIndex index = m_jobListModel->jobNumToIndex(jobNum);
616 if (index.isValid())
618 JobInfo job = m_jobListModel->getRow(index.row());
619 int seq = markerData.toInt();
620 job.sentenceNum = seq;
621 m_jobListModel->updateRow(index.row(), job);
622 m_ui->m_currentSentence->setPlainText(m_kspeech->getJobSentence(jobNum, seq));
625 if (KSpeech::mtSentenceEnd == markerType) {
626 m_ui->m_currentSentence->setPlainText(QString());
631 KttsJobMgrBrowserExtension::KttsJobMgrBrowserExtension(KttsJobMgrPart *parent)
632 : KParts::BrowserExtension(parent)
636 KttsJobMgrBrowserExtension::~KttsJobMgrBrowserExtension()