28#include <tqwhatsthis.h>
32#include <tqcombobox.h>
34#include <tqgroupbox.h>
35#include <tqlineedit.h>
36#include <tqtextedit.h>
37#include <tqlistview.h>
39#include <tqiconview.h>
41#include <tqgridview.h>
43#include <tqstylesheet.h>
46#include <tdeapplication.h>
49#include <dcopclient.h>
56#include "KoSpeaker.moc"
64 m_versionChecked(false),
69 m_prevPointerWidget(0),
75 m_cancelSpeakWidget(false)
79 TQValueList<uint> m_jobNums;
81 bool m_versionChecked;
83 TQString m_kttsdVersion;
87 TQString m_acceleratorPrefix;
97 TQWidget* m_prevPointerWidget;
99 TQWidget* m_prevFocusWidget;
101 TQWidget* m_prevWidget;
104 bool m_cancelSpeakWidget;
111KoSpeaker::KoSpeaker()
115 d = new KoSpeakerPrivate();
119KoSpeaker::~KoSpeaker()
121 if (d->m_jobNums.count() > 0) {
122 for ( int i = d->m_jobNums.count() - 1; i >= 0; i--)
123 removeText(d->m_jobNums[i]);
124 d->m_jobNums.clear();
132void KoSpeaker::probe()
139 w = kapp->focusWidget();
147 pos = TQCursor::pos();
148 w = kapp->widgetAt(pos, true);
154 d->m_timer->start(d->m_timeout);
159 if (!startKttsd()) return;
160 int jobCount = d->m_jobNums.count();
161 if (first && jobCount > 0) {
162 for ( int i = jobCount - 1; i >= 0; i--)
163 removeText(d->m_jobNums[i]);
164 d->m_jobNums.clear();
167 TQString s = msg.stripWhiteSpace();
168 if (s.isEmpty()) return;
171 TQString languageCode = langCode;
172 if (langCode.isEmpty())
173 languageCode = TDEGlobal::locale()->language();
179 if (getKttsdVersion().isEmpty())
180 d->m_jobNums.append(setText(s, languageCode));
182 if ((jobCount == 0) || (languageCode != d->m_langCode))
183 d->m_jobNums.append(setText(s, languageCode));
185 appendText(s, d->m_jobNums[jobCount-1]);
187 d->m_langCode = languageCode;
192 for (uint i = 0; i < d->m_jobNums.count(); i++)
193 startText(d->m_jobNums[i]);
200 config->setGroup( "TTS");
202 if (config->readBoolEntry( "SpeakPointerWidget", false)) d->m_speakFlags |= SpeakPointerWidget;
203 if (config->readBoolEntry( "SpeakFocusWidget", false)) d->m_speakFlags |= SpeakFocusWidget;
204 if (config->readBoolEntry( "SpeakTooltips", true)) d->m_speakFlags |= SpeakTooltip;
205 if (config->readBoolEntry( "SpeakWhatsThis", false)) d->m_speakFlags |= SpeakWhatsThis;
206 if (config->readBoolEntry( "SpeakDisabled", true)) d->m_speakFlags |= SpeakDisabled;
207 if (config->readBoolEntry( "SpeakAccelerators", true)) d->m_speakFlags |= SpeakAccelerator;
208 d->m_timeout = config->readNumEntry( "PollingInterval", 600);
209 d->m_acceleratorPrefix = config->readEntry( "AcceleratorPrefixWord", i18n( "Accelerator"));
212 d->m_timer = new TQTimer( this );
213 connect( d->m_timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(probe()) );
214 d->m_timer->start( d->m_timeout );
221 if (!w) return false;
226 if (w->inherits( "TQViewportWidget")) {
227 w = w->parentWidget();
228 if (!w) return false;
233 if ( w->inherits( "TQMenuBar") ) {
234 TQMenuBar* menuBar = dynamic_cast<TQMenuBar * >(w);
235 if (pos == TQPoint()) {
236 for (uint i = 0; i < menuBar->count(); ++i)
237 if (menuBar->isItemActive(menuBar->idAt(i))) {
238 id = menuBar->idAt(i);
245 text = menuBar->text( id);
248 if (w->inherits( "TQPopupMenu")) {
249 TQPopupMenu* popupMenu = dynamic_cast<TQPopupMenu * >(w);
250 if (pos == TQPoint()) {
251 for (uint i = 0; i < popupMenu->count(); ++i)
252 if (popupMenu->isItemActive(popupMenu->idAt(i))) {
253 id = popupMenu->idAt(i);
257 id = popupMenu->idAt(popupMenu->mapFromGlobal(pos));
259 text = popupMenu->text( id);
262 if (w->inherits( "TQTabBar")) {
263 TQTabBar* tabBar = dynamic_cast<TQTabBar * >(w);
265 if (pos == TQPoint())
266 tab = tabBar->tabAt(tabBar->currentTab());
268 tab = tabBar->selectTab(tabBar->mapFromGlobal(pos));
270 id = tab->identifier();
275 if (w->inherits( "TQListView")) {
276 TQListView* lv = dynamic_cast<TQListView * >(w);
277 TQListViewItem* item = 0;
278 if (pos == TQPoint())
279 item = lv->currentItem();
281 item = lv->itemAt(lv->viewport()->mapFromGlobal(pos));
283 id = lv->itemPos(item);
284 text = item->text(0);
285 for ( int col = 1; col < lv->columns(); ++col)
286 if (!item->text(col).isEmpty()) text += ". " + item->text(col);
290 if (w->inherits( "TQListBox")) {
291 TQListBox* lb = dynamic_cast<TQListBox * >(w);
293 TQListBoxItem* item = 0;
294 if (pos == TQPoint())
295 item = lb->item(lb->currentItem());
297 item = lb->itemAt(lb->mapFromGlobal(pos));
299 id = lb->index(item);
304 if (w->inherits( "TQIconView")) {
305 TQIconView* iv = dynamic_cast<TQIconView * >(w);
306 TQIconViewItem* item = 0;
307 if (pos == TQPoint())
308 item = iv->currentItem();
310 item = iv->findItem(iv->viewportToContents(iv->viewport()->mapFromGlobal(pos)));
317 if (w->inherits( "TQTable")) {
318 TQTable* tbl = dynamic_cast<TQTable * >(w);
321 if (pos == TQPoint()) {
322 row = tbl->currentRow();
323 col = tbl->currentColumn();
325 TQPoint p = tbl->viewportToContents(tbl->viewport()->mapFromGlobal(pos));
326 row = tbl->rowAt(p.y());
327 col = tbl->columnAt(p.x());
329 if (row >= 0 && col >= 0) {
330 id = (row * tbl->numCols()) + col;
331 text = tbl->text(row, col);
335 if (w->inherits( "TQGridView")) {
336 TQGridView* gv = dynamic_cast<TQGridView * >(w);
340 if (pos != TQPoint()) {
341 TQPoint p = gv->viewportToContents(gv->viewport()->mapFromGlobal(pos));
342 row = gv->rowAt(p.y());
343 col = gv->columnAt(p.x());
345 if (row >= 0 && col >= 0)
346 id = (row * gv->numCols()) + col;
349 if (pos == TQPoint()) {
350 if ( w == d->m_prevFocusWidget && id == d->m_prevFocusId) return false;
351 d->m_prevFocusWidget = w;
352 d->m_prevFocusId = id;
354 if ( w == d->m_prevPointerWidget && id == d->m_prevPointerId) return false;
355 d->m_prevPointerWidget = w;
356 d->m_prevPointerId = id;
358 if (w == d->m_prevWidget && id == d->m_prevId) return false;
364 d->m_cancelSpeakWidget = false;
366 if (d->m_cancelSpeakWidget) return true;
369 if ( w->inherits( "TQButton") )
370 text = dynamic_cast<TQButton * >(w)->text();
372 if (w->inherits( "TQComboBox"))
373 text = dynamic_cast<TQComboBox * >(w)->currentText();
375 if (w->inherits( "TQLineEdit"))
376 text = dynamic_cast<TQLineEdit * >(w)->text();
378 if (w->inherits( "TQTextEdit"))
379 text = dynamic_cast<TQTextEdit * >(w)->text();
381 if (w->inherits( "TQLabel"))
382 text = dynamic_cast<TQLabel * >(w)->text();
384 if (w->inherits( "TQGroupBox")) {
386 if (w->mapFromGlobal(pos).y() < 30)
387 text = dynamic_cast<TQGroupBox * >(w)->title();
394 text = text.stripWhiteSpace();
395 if (!text.isEmpty()) {
396 if (text.right(1) == ".")
405 TQString t = TQToolTip::textFor(w, pos);
406 t = t.stripWhiteSpace();
408 if (t.right(1) != ".") t += ".";
414 TQString t = TQWhatsThis::textFor(w, pos);
415 t = t.stripWhiteSpace();
417 if (t.right(1) != ".") t += ".";
424 text += i18n( "A grayed widget", "Disabled. ");
434 int amp = s.find( "&");
436 TQString acc = s.mid(++amp,1);
437 acc = acc.stripWhiteSpace();
439 s += ". " + d->m_acceleratorPrefix + " " + acc + ".";
443 if (TQStyleSheet::mightBeRichText(s)) {
445 s.replace(TQRegExp( "</?[pbius]>"), "");
446 s.replace(TQRegExp( "</?h\\d>"), "");
447 s.replace(TQRegExp( "<(br|hr)>"), " ");
449 "</?(qt|center|li|pre|div|span|em|strong|big|small|sub|sup|code|tt|font|nobr|ul|ol|dl|dt)>"), "");
450 s.replace(TQRegExp( "</?(table|tr|th|td).*>"), "");
451 s.replace(TQRegExp( "</?a\\s.+>"), "");
453 s.replace(TQRegExp( "<img\\s.*(?:source=|src=)\"([^|\"]+)[|]?([^|\"]*)\">"), "\\1 \\2 image. ");
455 if (s.isEmpty()) return false;
456 s.replace( "Ctrl+", i18n( "control plus "));
457 s.replace( "Alt+", i18n( "alt plus "));
458 s.replace( "+", i18n( " plus "));
459 sayScreenReaderOutput(s, "");
478 TDETrader::OfferList offers = TDETrader::self()->query( "DCOP/Text-to-Speech", "Name == 'KTTSD'");
479 return (offers.count() > 0);
482bool KoSpeaker::startKttsd()
489 if (kapp->startServiceByDesktopName( "kttsd", TQStringList(), &error)) {
490 kdDebug() << "KoSpeaker::startKttsd: error starting KTTSD service: " << error << endl;
491 d->m_enabled = false;
499TQString KoSpeaker::getKttsdVersion()
504 if (!d->m_versionChecked) {
505 DCOPClient *client = kapp->dcopClient();
508 TQByteArray replyData;
509 if ( client-> call( "kttsd", "KSpeech", "version()", data, replyType, replyData, true) ) {
510 TQDataStream arg(replyData, IO_ReadOnly);
511 arg >> d->m_kttsdVersion;
512 kdDebug() << "KoSpeaker::startKttsd: KTTSD version = " << d->m_kttsdVersion << endl;
514 d->m_versionChecked = true;
517 return d->m_kttsdVersion;
520void KoSpeaker::sayScreenReaderOutput( const TQString &msg, const TQString &talker)
522 if (msg.isEmpty()) return;
523 DCOPClient *client = kapp->dcopClient();
526 TQByteArray replyData;
527 TQDataStream arg(data, IO_WriteOnly);
528 arg << msg << talker;
529 if ( !client-> call( "kttsd", "KSpeech", "sayScreenReaderOutput(TQString,TQString)",
530 data, replyType, replyData, true) ) {
531 kdDebug() << "KoSpeaker::sayScreenReaderOutput: failed" << endl;
535uint KoSpeaker::setText( const TQString &text, const TQString &talker)
537 if (text.isEmpty()) return 0;
538 DCOPClient *client = kapp->dcopClient();
541 TQByteArray replyData;
542 TQDataStream arg(data, IO_WriteOnly);
543 arg << text << talker;
545 if ( !client-> call( "kttsd", "KSpeech", "setText(TQString,TQString)",
546 data, replyType, replyData, true) ) {
547 kdDebug() << "KoSpeaker::sayText: failed" << endl;
549 TQDataStream arg2(replyData, IO_ReadOnly);
555int KoSpeaker::appendText( const TQString &text, uint jobNum )
557 if (text.isEmpty()) return 0;
558 DCOPClient *client = kapp->dcopClient();
561 TQByteArray replyData;
562 TQDataStream arg(data, IO_WriteOnly);
563 arg << text << jobNum;
565 if ( !client-> call( "kttsd", "KSpeech", "appendText(TQString,uint)",
566 data, replyType, replyData, true) ) {
567 kdDebug() << "KoSpeaker::appendText: failed" << endl;
569 TQDataStream arg2(replyData, IO_ReadOnly);
575void KoSpeaker::startText(uint jobNum )
577 DCOPClient *client = kapp->dcopClient();
580 TQByteArray replyData;
581 TQDataStream arg(data, IO_WriteOnly);
583 if ( !client-> call( "kttsd", "KSpeech", "startText(uint)",
584 data, replyType, replyData, true) ) {
585 kdDebug() << "KoSpeaker::startText: failed" << endl;
589void KoSpeaker::removeText(uint jobNum )
591 DCOPClient *client = kapp->dcopClient();
594 TQByteArray replyData;
595 TQDataStream arg(data, IO_WriteOnly);
597 if ( !client-> call( "kttsd", "KSpeech", "removeText(uint)",
598 data, replyType, replyData, true) ) {
599 kdDebug() << "KoSpeaker::removeText: failed" << endl;
This file is part of the KDE/KOffice project.
bool isApplicationRegistered(const TQCString &remApp)
bool call(const TQCString &remApp, const TQCString &remObj, const TQCString &remFun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData, bool useEventLoop, int timeout, bool forceRemote)
KoSpeaker is a singleton object that provides Text-to-Speech services for KOffice applications.
bool sayWidget(const TQString &msg) Speak a msg that came from a widget, such as the widget's text label, tool tip, etc.
void customSpeakWidget(TQWidget *w, const TQPoint &p, uint flags) This signal is emitted each polling interval when KoSpeaker did not speak the widget (either because ...
void customSpeakNewWidget(TQWidget *w, const TQPoint &p, uint flags) This signal is emitted whenever a new widget has received focus or the mouse pointer has moved to a n...
void readConfig(TDEConfig *config) Reads configuration options from config object and starts TTS if screen reader capability is requeste...
void startSpeech() Start speaking queued text job (if any).
void queueSpeech(const TQString &msg, const TQString &langCode=TQString(), bool first=true) Queue a msg as a speech text job.
bool isEnabled() const Returns true if TTS services are available.
@ SpeakWhatsThis Speak Whats This if available.
@ SpeakTooltip Speak tooltip if available.
@ SpeakDisabled Say 'disabled' if not enabled.
@ SpeakPointerWidget Speak widget under mouse pointer.
@ SpeakFocusWidget Speak widget with focus.
@ SpeakAccelerator Speak accelerator.
bool maybeSayWidget(TQWidget *w, const TQPoint &pos=TQPoint()) Given a widget w and its pos screen coordinates, tries to extract the text of the widget and speak it...
static bool isKttsdInstalled() Returns whether the KTTSD deamon is installed in the system.
|