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.
5 Copyright : (C) 2004,2005 by Gary Cramblitt <garycramblitt@comcast.net>
6 Copyright : (C) 2009 by Jeremy Whiting <jeremy@scitools.com>
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"
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>
41 #include <kcomponentdata.h>
43 #include <kaboutdata.h>
46 #include <kencodingfiledialog.h>
47 #include <kinputdialog.h>
48 #include <ktextedit.h>
50 #include <kdemacros.h>
51 #include <kparts/genericfactory.h>
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");
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
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.
140 connect(m_ui
->m_jobListView
, SIGNAL(clicked(const QModelIndex
&)),
141 this, SLOT(slot_jobListView_clicked()));
143 // Fill the Job List.
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");
171 delete m_jobListModel
;
174 bool KttsJobMgrPart::openFile()
179 bool KttsJobMgrPart::closeUrl()
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()
201 void KttsJobMgrPart::slot_job_resume()
206 void KttsJobMgrPart::slot_job_restart()
208 int jobNum
= getCurrentJobNum();
209 // kDebug() << "KttsJobMgrPart::slot_job_restart: jobNum = " << jobNum;
212 int seq
= m_kspeech
->moveRelSentence(jobNum
, 0);
213 m_kspeech
->moveRelSentence(jobNum
, -seq
);
218 void KttsJobMgrPart::slot_job_prev_sen()
220 int jobNum
= getCurrentJobNum();
223 m_kspeech
->moveRelSentence(jobNum
, -1);
228 void KttsJobMgrPart::slot_job_next_sen()
230 int jobNum
= getCurrentJobNum();
233 m_kspeech
->moveRelSentence(jobNum
, 1);
238 void KttsJobMgrPart::slot_job_remove()
240 int jobNum
= getCurrentJobNum();
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();
259 m_kspeech
->moveJobLater(jobNum
);
261 // Select the job we just moved.
262 QModelIndex index
= m_jobListModel
->jobNumToIndex(jobNum
);
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();
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
);
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
);
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.
301 KSpeech::SayOptions sayOptions
= KSpeech::soNone
;
302 const QMimeData
* data
= cb
->mimeData();
305 if (data
->hasFormat("text/html"))
307 // if (m_kspeech->supportsMarkup(NULL, KSpeech::mtHtml))
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");
317 sayOptions
= KSpeech::soSsml
;
321 if (text
.isEmpty()) {
323 sayOptions
= KSpeech::soPlainText
;
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();
356 // Select the previously-selected job.
359 QModelIndex index
= m_jobListModel
->jobNumToIndex(jobNum
);
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()
377 QModelIndex index
= m_ui
->m_jobListView
->currentIndex();
379 jobNum
= m_jobListModel
->getRow(index
.row()).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
);
397 qint32 sentenceCount
;
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
;
408 job
->sentenceNum
= sentenceNum
;
409 job
->sentenceCount
= sentenceCount
;
410 job
->talkerID
= cachedTalkerCodeToTalkerID(talkerCode
);
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
);
426 m_jobListModel
->updateRow(index
.row(), *job
);
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
);
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);
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.
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
];
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;
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
);
504 (i
.next())->setEnabled( enable
);
508 // Later button only enables if currently selected list item is not bottom of list.
509 QModelIndex index
= m_ui
->m_jobListView
->currentIndex();
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
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
)
538 case KSpeech::jsQueued
:
540 QModelIndex index
= m_jobListModel
->jobNumToIndex(jobNum
);
545 JobInfo
* job
= retrieveJobInfo(jobNum
);
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();
561 case KSpeech::jsSpeakable
:
563 QModelIndex index
= m_jobListModel
->jobNumToIndex(jobNum
);
564 if (index
.isValid()) {
565 JobInfo
* job
= retrieveJobInfo(jobNum
);
567 m_jobListModel
->updateRow(index
.row(), *job
);
569 m_jobListModel
->removeRow(index
.row());
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
);
582 JobInfo job
= m_jobListModel
->getRow(index
.row());
584 m_jobListModel
->updateRow(index
.row(), job
);
586 m_ui
->m_currentSentence
->setPlainText(QString());
589 case KSpeech::jsDeleted
:
591 QModelIndex index
= m_jobListModel
->jobNumToIndex(jobNum
);
593 m_jobListModel
->removeRow(index
.row());
594 autoSelectInJobListView();
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
)
614 if (KSpeech::mtSentenceBegin
== markerType
) {
615 QModelIndex index
= m_jobListModel
->jobNumToIndex(jobNum
);
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()