1/* This file is part of the KDE project
2 * Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "kaccessibleapp.h"
21#include "kaccessibleinterface.h"
22
23#include <QMainWindow>
24#include <QMenu>
25#include <QLayout>
26#include <QTimer>
27#include <QStack>
28#include <QTextStream>
29#include <QLabel>
30#include <QCheckBox>
31#include <QTreeWidget>
32#include <QTreeWidgetItem>
33#include <QClipboard>
34#include <QMutex>
35#include <QMutexLocker>
36#include <QDBusConnection>
37#include <QDBusInterface>
38#include <QDBusPendingCall>
39#include <QDBusArgument>
40#include <QDBusMetaType>
41#include <kmainwindow.h>
42#include <kconfig.h>
43#include <kcombobox.h>
44#include <kconfiggroup.h>
45#include <klocale.h>
46#include <kicon.h>
47#include <kaboutdata.h>
48#include <kcmdlineargs.h>
49#include <kinputdialog.h>
50#include <kaction.h>
51#include <ktoggleaction.h>
52#include <kactioncollection.h>
53#include <ksystemtrayicon.h>
54#include <kdebug.h>
55#include <kpagewidget.h>
56#include <kpagewidgetmodel.h>
57
58#if defined(SPEECHD_FOUND)
59#include <libspeechd.h>
60#endif
61
62Q_GLOBAL_STATIC(Speaker, speaker)
63
64class Speaker::Private
65{
66 public:
67 bool m_isSpeaking;
68 int m_voiceType;
69 QStack< QPair<QString,Speaker::Priority> > m_sayStack;
70 QMutex m_mutex;
71#if defined(SPEECHD_FOUND)
72 SPDConnection *m_connection;
73#endif
74 explicit Private()
75 : m_isSpeaking(false)
76 , m_voiceType(1)
77#if defined(SPEECHD_FOUND)
78 , m_connection(0)
79#endif
80 {
81 }
82#if defined(SPEECHD_FOUND)
83 static void speechdCallback(size_t msg_id, size_t client_id, SPDNotificationType state)
84 {
85 Q_UNUSED(msg_id);
86 Q_UNUSED(client_id);
87 switch(state) {
88 case SPD_EVENT_BEGIN:
89 Speaker::instance()->setSpeaking(true);
90 break;
91 case SPD_EVENT_END:
92 Speaker::instance()->setSpeaking(false);
93 QTimer::singleShot(0, Speaker::instance(), SLOT(sayNext()));
94 break;
95 case SPD_EVENT_CANCEL:
96 Speaker::instance()->setSpeaking(false);
97 Speaker::instance()->clearSayStack();
98 break;
99 case SPD_EVENT_PAUSE:
100 break;
101 case SPD_EVENT_RESUME:
102 break;
103 case SPD_EVENT_INDEX_MARK:
104 break;
105 }
106 }
107#endif
108};
109
110Speaker::Speaker()
111 : d(new Private)
112{
113}
114
115Speaker::~Speaker()
116{
117 disconnect();
118 delete d;
119}
120
121Speaker* Speaker::instance()
122{
123 return speaker();
124}
125
126bool Speaker::isConnected() const
127{
128#if defined(SPEECHD_FOUND)
129 return d->m_connection;
130#else
131 return true;
132#endif
133}
134
135void Speaker::disconnect()
136{
137#if defined(SPEECHD_FOUND)
138 if(d->m_connection) {
139 spd_set_notification_off(d->m_connection, SPD_BEGIN);
140 spd_set_notification_off(d->m_connection, SPD_END);
141 spd_set_notification_off(d->m_connection, SPD_CANCEL);
142 spd_set_notification_off(d->m_connection, SPD_PAUSE);
143 spd_set_notification_off(d->m_connection, SPD_RESUME);
144 d->m_connection->callback_begin = d->m_connection->callback_end = d->m_connection->callback_cancel = d->m_connection->callback_pause = d->m_connection->callback_resume = 0;
145 spd_cancel_all(d->m_connection);
146 spd_close(d->m_connection);
147 d->m_connection = 0;
148 d->m_isSpeaking = false;
149 d->m_sayStack.clear();
150 }
151#endif
152}
153
154bool Speaker::reconnect()
155{
156 disconnect();
157
158#if defined(SPEECHD_FOUND)
159 d->m_connection = spd_open("kaccessible", "main", NULL, SPD_MODE_THREADED); //SPD_MODE_SINGLE);
160 if( ! d->m_connection) {
161 kWarning() << "Failed to connect with speech-dispatcher";
162 return false;
163 }
164
165 d->m_connection->callback_begin = d->m_connection->callback_end = d->m_connection->callback_cancel = d->m_connection->callback_pause = d->m_connection->callback_resume = Private::speechdCallback;
166 spd_set_notification_on(d->m_connection, SPD_BEGIN);
167 spd_set_notification_on(d->m_connection, SPD_END);
168 spd_set_notification_on(d->m_connection, SPD_CANCEL);
169 spd_set_notification_on(d->m_connection, SPD_PAUSE);
170 spd_set_notification_on(d->m_connection, SPD_RESUME);
171#endif
172
173 setVoiceType(d->m_voiceType);
174 return true;
175}
176
177bool Speaker::isSpeaking() const
178{
179 return d->m_isSpeaking;
180}
181
182void Speaker::setSpeaking(bool speaking)
183{
184 d->m_isSpeaking = speaking;
185}
186
187void Speaker::cancel()
188{
189 QMutexLocker locker(&d->m_mutex);
190 d->m_sayStack.clear();
191#if defined(SPEECHD_FOUND)
192 if(d->m_connection) {
193 spd_cancel_all(d->m_connection);
194 }
195#endif
196}
197
198bool Speaker::say(const QString& text, Priority priority)
199{
200 QMutexLocker locker(&d->m_mutex);
201 if(!isConnected())
202 return false;
203 d->m_sayStack.push( QPair<QString,Speaker::Priority>(text,priority) );
204 if(!d->m_isSpeaking)
205 QTimer::singleShot(0, this, SLOT(sayNext()));
206 return true;
207}
208
209void Speaker::sayNext()
210{
211 QMutexLocker locker(&d->m_mutex);
212 if(d->m_sayStack.isEmpty()) {
213 return;
214 }
215 QPair<QString,Speaker::Priority> p = d->m_sayStack.pop();
216#if defined(SPEECHD_FOUND)
217 if(d->m_connection) {
218 SPDPriority spdpriority = (SPDPriority) p.second;
219 int msg_id = spd_say(d->m_connection, spdpriority, p.first.toUtf8().data());
220 if(msg_id == -1) {
221 kWarning() << "Failed to say text=" << p.first;
222 }
223 }
224#else
225 //QDBusInterface iface("org.kde.jovie","/KSpeech");
226 //iface.asyncCall("say", text, 0);
227#endif
228}
229
230char** Speaker::modules() const
231{
232#if defined(SPEECHD_FOUND)
233 if(d->m_connection)
234 return spd_list_modules(d->m_connection);
235#endif
236 return NULL;
237}
238
239char** Speaker::voices() const
240{
241#if defined(SPEECHD_FOUND)
242 if(d->m_connection)
243 return spd_list_voices(d->m_connection);
244#endif
245 return NULL;
246}
247
248QStringList Speaker::languages() const
249{
250 QStringList result;
251#if defined(SPEECHD_FOUND)
252 if(d->m_connection) {
253 SPDVoice** voices = spd_list_synthesis_voices(d->m_connection);
254 while(voices && voices[0]) {
255 const QString lng = QString::fromLatin1(voices[0]->language);
256 if(!lng.isEmpty() && !result.contains(lng)) result.append(lng);
257 ++voices;
258 }
259 }
260#endif
261 return result;
262}
263
264int Speaker::voiceType() const
265{
266 return d->m_voiceType;
267}
268
269void Speaker::setVoiceType(int type)
270{
271 d->m_voiceType = type;
272#if defined(SPEECHD_FOUND)
273 if(d->m_connection) {
274 spd_set_voice_type_all(d->m_connection, (SPDVoiceType) type);
275 }
276#endif
277}
278
279void Speaker::clearSayStack()
280{
281 d->m_sayStack.clear();
282}
283
284class Adaptor::Private
285{
286 public:
287 bool m_speechEnabled;
288 explicit Private() : m_speechEnabled(false) {}
289};
290
291Adaptor::Adaptor(QObject *parent)
292 : QObject(parent)
293 , d(new Private)
294{
295 KConfig config(QLatin1String( "kaccessibleapp" ));
296 KConfigGroup group = config.group("Main");
297 d->m_speechEnabled = group.readEntry("SpeechEnabled", d->m_speechEnabled);
298
299 const int prevVoiceType = Speaker::instance()->voiceType();
300 const int newVoiceType = group.readEntry("VoiceType", prevVoiceType);
301 if(prevVoiceType != newVoiceType)
302 Speaker::instance()->setVoiceType(newVoiceType);
303}
304
305Adaptor::~Adaptor()
306{
307 delete d;
308}
309
310void Adaptor::setFocusChanged(const KAccessibleInterface& iface)
311{
312 int px = -1;
313 int py = -1;
314 QRect r = iface.rect;
315 emit focusChanged(px, py, r.x(), r.y(), r.width(), r.height());
316
317 emit notified(QAccessible::Focus, iface);
318
319 QString text = iface.name;
320 /*
321 if(!iface.accelerator.isEmpty()) {
322 QString s = iface.accelerator;
323 s = s.replace("+"," ");
324 text += " " + s;
325 }
326 */
327 sayText(text);
328}
329
330void Adaptor::setValueChanged(const KAccessibleInterface& iface)
331{
332 sayText(iface.value);
333}
334
335void Adaptor::setAlert(const KAccessibleInterface& iface)
336{
337 Speaker::instance()->cancel();
338 sayText(iface.name, int(Speaker::Message));
339}
340
341void Adaptor::sayText(const QString& text, int priority)
342{
343 if(d->m_speechEnabled && !text.isEmpty() && (Speaker::instance()->isConnected() || Speaker::instance()->reconnect())) {
344 Speaker::instance()->say(text, Speaker::Priority(priority));
345 }
346}
347
348bool Adaptor::speechEnabled() const
349{
350 return d->m_speechEnabled;
351}
352
353void Adaptor::setSpeechEnabled(bool enabled)
354{
355 if(d->m_speechEnabled == enabled)
356 return;
357
358 d->m_speechEnabled = enabled;
359
360 KConfig config(QLatin1String( "kaccessibleapp" ));
361 KConfigGroup group = config.group("Main");
362 group.writeEntry("SpeechEnabled", d->m_speechEnabled);
363
364 if(!d->m_speechEnabled) {
365 Speaker::instance()->cancel();
366 }
367
368 emit speechEnabledChanged(d->m_speechEnabled);
369}
370
371int Adaptor::voiceType() const
372{
373 return Speaker::instance()->voiceType();
374}
375
376void Adaptor::setVoiceType(int type)
377{
378 if(type == Speaker::instance()->voiceType())
379 return;
380
381 KConfig config(QLatin1String( "kaccessibleapp" ));
382 KConfigGroup group = config.group("Main");
383 group.writeEntry("VoiceType", type);
384
385 Speaker::instance()->setVoiceType(type);
386}
387
388class KAccessibleApp::Private
389{
390 public:
391 Adaptor *m_adaptor;
392 QMap<QString, KAction*> m_collection;
393 explicit Private() : m_adaptor(0) {}
394 ~Private() { qDeleteAll(m_collection); }
395};
396
397KAccessibleApp::KAccessibleApp()
398 : KUniqueApplication()
399 , d(new Private)
400{
401 qDBusRegisterMetaType<KAccessibleInterface>();
402
403 setWindowIcon(KIcon(QLatin1String( "preferences-desktop-accessibility" )));
404 setQuitOnLastWindowClosed(false);
405
406 d->m_adaptor = new Adaptor(this);
407 if( ! QDBusConnection::sessionBus().registerObject(QLatin1String( "/Adaptor" ), d->m_adaptor, QDBusConnection::ExportAllContents)) {
408 kWarning() << "Unable to register KAccessibleApp to dbus";
409 QTimer::singleShot(0, this, SLOT(quit()));
410 } else {
411 KToggleAction* enableScreenreaderAction = new KToggleAction(this);
412 enableScreenreaderAction->setText(i18n("Enable Screenreader"));
413 enableScreenreaderAction->setIcon(KIcon(QLatin1String( "text-speak" )));
414 enableScreenreaderAction->setChecked(d->m_adaptor->speechEnabled());
415 connect(enableScreenreaderAction, SIGNAL(triggered(bool)), this, SLOT(enableScreenreader(bool)));
416 connect(d->m_adaptor, SIGNAL(speechEnabledChanged(bool)), enableScreenreaderAction, SLOT(setChecked(bool)));
417 d->m_collection.insert(QLatin1String( "enableScreenreader" ), enableScreenreaderAction);
418
419 KAction* speakTextAction = new KAction(this);
420 speakTextAction->setText(i18n("Speak Text..."));
421 speakTextAction->setIcon(KIcon(QLatin1String( "text-plain" )));
422 connect(speakTextAction, SIGNAL(triggered(bool)), this, SLOT(speakText()));
423 d->m_collection.insert(QLatin1String( "speakText" ), speakTextAction);
424
425 KAction* speakClipboardAction = new KAction(this);
426 speakClipboardAction->setText(i18n("Speak Clipboard"));
427 speakClipboardAction->setIcon(KIcon(QLatin1String( "klipper" )));
428 connect(speakClipboardAction, SIGNAL(triggered(bool)), this, SLOT(speakClipboard()));
429 d->m_collection.insert(QLatin1String( "speakClipboard" ), speakClipboardAction);
430 }
431}
432
433KAccessibleApp::~KAccessibleApp()
434{
435 delete d;
436}
437
438Adaptor* KAccessibleApp::adaptor() const
439{
440 return d->m_adaptor;
441}
442
443KAction* KAccessibleApp::action(const QString &name) const
444{
445 return d->m_collection.contains(name) ? d->m_collection[name] : 0;
446}
447
448void KAccessibleApp::enableScreenreader(bool enabled)
449{
450 d->m_adaptor->setSpeechEnabled(enabled);
451}
452
453void KAccessibleApp::speakClipboard()
454{
455 const QString text = clipboard()->text();
456 if(!text.isEmpty()) {
457 //Speaker::instance()->cancel();
458 Speaker::instance()->say(text);
459 }
460}
461
462void KAccessibleApp::speakText()
463{
464 const QString text = KInputDialog::getText(i18n("Speak Text"), i18n("Type the text and press OK to speak the text."));
465 if(!text.isEmpty()) {
466 //Speaker::instance()->cancel();
467 Speaker::instance()->say(text);
468 }
469}
470
471/// class for the icon shown in the systemtray.
472class SystemTray : public KSystemTrayIcon
473{
474 public:
475 explicit SystemTray(KAccessibleApp *app, QWidget* parent)
476 : KSystemTrayIcon(QLatin1String( "preferences-desktop-accessibility" ), parent)
477 {
478 //QAction *titleAction = contextMenuTitle();
479 //titleAction->setText(i18n("Accessibility Bridge"));
480 //titleAction->setIcon(KIcon("preferences-desktop-accessibility"));
481 //setContextMenuTitle(titleAction);
482
483 //QAction* fileQuitAction = actionCollection()->action("file_quit");
484 //if(fileQuitAction) delete actionCollection()->takeAction(fileQuitAction);
485
486 foreach(const QString &name, QStringList() << QLatin1String( "enableScreenreader" ) << QLatin1String( "speakText" ) << QLatin1String( "speakClipboard" ))
487 if(KAction* action = app->action(name))
488 contextMenu()->addAction(action);
489
490 //QMenu *popup = dynamic_cast<QMenu*>( KAccessibleApp::App->factory()->container("systemtray_actions", KAccessibleApp::App) );
491 //if (popup) contextMenu()->addActions( popup->actions() );
492 }
493 virtual ~SystemTray()
494 {
495 }
496 protected:
497 bool event(QEvent *event)
498 {
499 /*
500 if( event->type() == QEvent::ToolTip ) {
501 QHelpEvent *helpEvent = static_cast<QHelpEvent*>(event);
502 QToolTip::showText(helpEvent->globalPos(), QString("<b>Accessibility Bridge</b><br>%1").arg(tooltip));
503 //QToolTip::hideText();
504 }
505 */
506 return KSystemTrayIcon::event(event);
507 }
508};
509
510class MainWindow::Private
511{
512 public:
513 KAccessibleApp *m_app;
514 Adaptor *m_adaptor;
515 SystemTray *m_systemtray;
516 KPageWidget *m_pageTab;
517 KPageWidgetModel *m_pageModel;
518 KComboBox* m_voiceTypeCombo;
519 QTreeWidget *m_logs;
520 bool m_hideMainWin;
521 bool m_logEnabled;
522
523 explicit Private(KAccessibleApp *app) : m_app(app), m_adaptor(app->adaptor()), m_systemtray(0), m_pageTab(0), m_pageModel(0), m_voiceTypeCombo(0), m_logs(0), m_hideMainWin(false), m_logEnabled(false) {}
524
525 void addPage(QWidget* page, const QIcon& iconset, const QString& label)
526 {
527 QWidget* p = dynamic_cast<QWidget*>(page);
528 if (!p) return;
529 KPageWidgetItem* item = m_pageModel->addPage(p, QLatin1String( "" )); //p->objectName());
530 item->setName(label);
531 item->setHeader(QLatin1String( "" )); // hide the header
532 item->setIcon(KIcon(iconset));
533 QModelIndex index = m_pageModel->index(item);
534 Q_ASSERT(index.isValid());
535 m_pageModel->setData(index, QVariant(), Qt::DisplayRole);
536 m_pageModel->setData(index, QVariant(), KPageModel::HeaderRole);
537 //pages.append(page);
538 //page->plugGenericActions(this, SLOT(pageActionsChanged(KMLDonkeyPage*)));
539 }
540};
541
542MainWindow::MainWindow(KAccessibleApp *app)
543 : KMainWindow()
544 , d(new Private(app))
545{
546 KConfig config(QLatin1String( "kaccessibleapp" ));
547 KConfigGroup group = config.group("Main");
548 d->m_logEnabled = group.readEntry("LogEnabled", d->m_logEnabled);
549
550 const int prevVoiceType = Speaker::instance()->voiceType();
551 const int newVoiceType = group.readEntry("VoiceType", prevVoiceType);
552 if(prevVoiceType != newVoiceType)
553 Speaker::instance()->setVoiceType(newVoiceType);
554
555 d->m_systemtray = new SystemTray(app, this);
556 d->m_systemtray->show();
557
558 d->m_pageTab = new KPageWidget(this);
559 d->m_pageTab->setFaceType( KPageView::Tabbed ); //Auto,Plain,List,Tree,Tabbed
560 d->m_pageTab->layout()->setMargin(0);
561
562 d->m_pageModel = new KPageWidgetModel(d->m_pageTab);
563 d->m_pageTab->setModel(d->m_pageModel);
564
565 QWidget* readerPage = new QWidget(d->m_pageTab);
566 QGridLayout* readerLayout = new QGridLayout(readerPage);
567 readerLayout->setMargin(0);
568 readerPage->setLayout(readerLayout);
569 QCheckBox *enableReader = new QCheckBox(i18n("Enable Screenreader"));
570 readerLayout->addWidget(enableReader,0,0,1,2);
571#if defined(SPEECHD_FOUND)
572 enableReader->setChecked(d->m_adaptor->speechEnabled());
573 connect(d->m_adaptor, SIGNAL(speechEnabledChanged(bool)), enableReader, SLOT(setChecked(bool)));
574 connect(enableReader, SIGNAL(stateChanged(int)), this, SLOT(enableReaderChanged(int)));
575
576 QLabel *voiceTypeLabel = new QLabel(i18n("Voice Type:"), readerPage);
577 readerLayout->addWidget(voiceTypeLabel,1,0);
578 d->m_voiceTypeCombo = new KComboBox(this);
579 voiceTypeLabel->setBuddy(d->m_voiceTypeCombo);
580 d->m_voiceTypeCombo->addItem(i18n("Male 1"), SPD_MALE1);
581 d->m_voiceTypeCombo->addItem(i18n("Male 2"), SPD_MALE2);
582 d->m_voiceTypeCombo->addItem(i18n("Male 3"), SPD_MALE3);
583 d->m_voiceTypeCombo->addItem(i18n("Female 1"), SPD_FEMALE1);
584 d->m_voiceTypeCombo->addItem(i18n("Female 2"), SPD_FEMALE2);
585 d->m_voiceTypeCombo->addItem(i18n("Female 3"), SPD_FEMALE3);
586 d->m_voiceTypeCombo->addItem(i18n("Boy"), SPD_CHILD_MALE);
587 d->m_voiceTypeCombo->addItem(i18n("Girl"), SPD_CHILD_FEMALE);
588 for(int i = 0; i < d->m_voiceTypeCombo->count(); ++i)
589 if(d->m_voiceTypeCombo->itemData(i).toInt() == Speaker::instance()->voiceType())
590 d->m_voiceTypeCombo->setCurrentIndex(i);
591 connect(d->m_voiceTypeCombo, SIGNAL(activated(int)), this, SLOT(voiceTypeChanged(int)));
592 readerLayout->addWidget(d->m_voiceTypeCombo,1,1);
593
594 readerLayout->setRowStretch(2,1);
595#else
596 enableReader->setEnabled(false);
597 readerLayout->setRowStretch(1,1);
598#endif
599 readerLayout->setColumnStretch(2,1);
600 d->addPage(readerPage, KIcon(QLatin1String( "text-speak" )), i18n("Screenreader"));
601
602 QWidget* logsPage = new QWidget(d->m_pageTab);
603 QVBoxLayout* logsLayout = new QVBoxLayout(logsPage);
604 logsLayout->setMargin(0);
605 logsPage->setLayout(logsLayout);
606 QCheckBox *enableLogsCheckbox = new QCheckBox(i18n("Enable Logs"));
607 logsLayout->addWidget(enableLogsCheckbox);
608 d->m_logs = new QTreeWidget(logsPage);
609 d->m_logs->setColumnCount(10);
610 d->m_logs->setHeaderLabels(QStringList() << i18n("Reason") << i18n("Type") << i18n("Class") << i18n("Name") << i18n("Value") << i18n("Accelerator") << i18n("State") << i18n("Rect") << i18n("Object") << i18n("Description"));
611 d->m_logs->setRootIsDecorated(false);
612 d->m_logs->setEnabled(false);
613 if(d->m_logEnabled) {
614 enableLogsCheckbox->setChecked(Qt::Checked);
615 enableLogs(Qt::Checked);
616 }
617 connect(enableLogsCheckbox, SIGNAL(stateChanged(int)), this, SLOT(enableLogs(int)));
618 logsLayout->addWidget(d->m_logs);
619 d->addPage(logsPage, KIcon(QLatin1String( "view-list-text" )), i18n("Logs"));
620
621 setCentralWidget(d->m_pageTab);
622 resize(QSize(460, 320).expandedTo(minimumSizeHint()));
623 setAutoSaveSettings();
624}
625
626MainWindow::~MainWindow()
627{
628 delete d;
629}
630
631// #### WRONG, show() is not virtual in QWidget (since Qt 4.0)
632void MainWindow::show()
633{
634 if (!d->m_hideMainWin) {
635 d->m_hideMainWin = false;
636 KMainWindow::show();
637 }
638}
639
640// Deprecated, won't be called in KF5. Replace with closeEvent, possibly.
641bool MainWindow::queryExit()
642{
643 d->m_hideMainWin = isHidden();
644 return true;
645}
646
647bool MainWindow::queryClose()
648{
649 if (!QObject::sender()) { // ### Looks very fishy, what is this really testing for?
650 hide();
651 return false;
652 }
653 return true;
654}
655
656void MainWindow::notified(int reason, const KAccessibleInterface& iface)
657{
658 QTreeWidgetItem *root = d->m_logs->invisibleRootItem();
659 if(root->childCount() > 1000) delete root->takeChild(0);
660
661 QTreeWidgetItem *child = new QTreeWidgetItem(root);
662 child->setText(1, iface.className);
663 child->setText(2, iface.name);
664 child->setText(3, iface.value);
665 child->setText(4, iface.accelerator);
666 child->setText(5, stateToString(iface.state));
667 child->setText(6, QString(QLatin1String("%1,%2,%3,%4")).arg(iface.rect.x()).arg(iface.rect.y()).arg(iface.rect.width()).arg(iface.rect.height()));
668 child->setText(7, iface.objectName);
669 child->setText(8, iface.description);
670 d->m_logs->scrollToItem(child);
671}
672
673void MainWindow::enableLogs(int state)
674{
675 const bool logEnabled = state == Qt::Checked;
676 if(logEnabled) {
677 connect(d->m_app->adaptor(), SIGNAL(notified(int,KAccessibleInterface)), this, SLOT(notified(int,KAccessibleInterface)));
678 } else {
679 disconnect(d->m_app->adaptor(), SIGNAL(notified(int,KAccessibleInterface)), this, SLOT(notified(int,KAccessibleInterface)));
680 d->m_logs->clear();
681 }
682
683 d->m_logs->setEnabled(logEnabled);
684
685 if(d->m_logEnabled != logEnabled) {
686 d->m_logEnabled = logEnabled;
687 KConfig config(QLatin1String( "kaccessibleapp" ));
688 KConfigGroup group = config.group("Main");
689 group.writeEntry("LogEnabled", d->m_logEnabled);
690 }
691}
692
693void MainWindow::enableReaderChanged(int state)
694{
695 d->m_adaptor->setSpeechEnabled(state == Qt::Checked);
696}
697
698void MainWindow::voiceTypeChanged(int index)
699{
700 d->m_adaptor->setVoiceType(d->m_voiceTypeCombo->itemData(index).toInt());
701}
702
703int main(int argc, char *argv[])
704{
705 KAboutData aboutData("kaccessibleapp", "",
706 ki18n("KDE Accessible"), "0.4",
707 ki18n("KDE Accessible"), KAboutData::License_GPL,
708 ki18n("(c) 2010, 2011 Sebastian Sauer"));
709 aboutData.addAuthor(ki18n("Sebastian Sauer"), ki18n("Maintainer"), "sebastian.sauer@kdab.com");
710 KCmdLineArgs::init(argc, argv, &aboutData);
711 KUniqueApplication::addCmdLineOptions();
712 if (!KUniqueApplication::start()) {
713 fprintf(stderr, "kaccessibleapp is already running!\n");
714 return 0;
715 }
716
717 KAccessibleApp app;
718
719 MainWindow window(&app);
720 //window.show();
721
722 return app.exec();
723}
724