Reenabled text to speech using QtSpeech as an optional dependency.

If QtSpeech (dev branch) is built and installed this builds and works
very well.
REVIEW:122553
frameworks
Jeremy Whiting 12 years ago
parent 5c85b83a8f
commit 61554c833e
  1. 17
      CMakeLists.txt
  2. 2
      conf/okular.kcfg
  3. 31
      part.cpp
  4. 117
      ui/pageview.cpp
  5. 2
      ui/pageview.h
  6. 107
      ui/tts.cpp
  7. 11
      ui/tts.h

@ -19,6 +19,13 @@ include(ECMAddTests)
find_package(Qt5 CONFIG REQUIRED COMPONENTS Core DBus Test Widgets PrintSupport Svg Qml Quick)
find_package(Qt5 OPTIONAL_COMPONENTS TextToSpeech)
if (NOT Qt5TextToSpeech_FOUND)
message(STATUS "Qt5TextToSpeech not found, speech features will be disabled")
else()
add_definitions(-DHAVE_SPEECH)
endif()
find_package(KF5 REQUIRED COMPONENTS
Activities
Archive
@ -253,10 +260,14 @@ set(okularpart_SRCS
ui/toc.cpp
ui/tocmodel.cpp
ui/toolaction.cpp
# ui/tts.cpp
ui/videowidget.cpp
)
if (Qt5TextToSpeech_FOUND)
set(okularpart_SRCS ${okularpart_SRCS}
ui/tts.cpp)
endif()
ki18n_wrap_ui(okularpart_SRCS
conf/dlgaccessibilitybase.ui
conf/dlgeditorbase.ui
@ -268,7 +279,6 @@ ki18n_wrap_ui(okularpart_SRCS
kconfig_add_kcfg_files(okularpart_SRCS conf/settings.kcfgc )
message("KF5: Enable ui/tts.cpp again")
#qt5_add_dbus_interfaces(okularpart_SRCS ${KDE4_DBUS_INTERFACES_DIR}/org.kde.KSpeech.xml)
add_library(okularpart MODULE ${okularpart_SRCS})
@ -276,6 +286,9 @@ generate_export_header(okularpart BASE_NAME okularpart)
target_link_libraries(okularpart okularcore Qt5::Svg KF5::Parts ${MATH_LIB} Phonon::phonon4qt5 KF5::Solid)
if (Qt5TextToSpeech_FOUND)
target_link_libraries(okularpart Qt5::TextToSpeech)
endif()
install(TARGETS okularpart DESTINATION ${PLUGIN_INSTALL_DIR})

@ -95,7 +95,7 @@
<choice name="Path" />
</choices>
</entry>
<entry key="UseKTTSD" type="Bool" />
<entry key="UseTTS" type="Bool" />
<entry key="WatchFile" type="Bool" >
<default>true</default>
</entry>

@ -304,7 +304,7 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
}
}
Okular::Settings::instance( configFileName );
numberOfParts++;
if (numberOfParts == 1) {
QDBusConnection::sessionBus().registerObject("/okular", this, QDBusConnection::ExportScriptableSlots);
@ -506,10 +506,11 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
// keep us informed when the user changes settings
connect( Okular::Settings::self(), SIGNAL(configChanged()), this, SLOT(slotNewConfig()) );
// [SPEECH] check for KTTSD presence and usability
const KService::Ptr kttsd = KService::serviceByDesktopName("kttsd");
Okular::Settings::setUseKTTSD( kttsd );
#ifdef HAVE_SPEECH
// [SPEECH] check for TTS presence and usability
Okular::Settings::setUseTTS( true );
Okular::Settings::self()->save();
#endif
rebuildBookmarkMenu( false );
@ -1708,7 +1709,7 @@ void Part::slotFileDirty( const QString& path )
void Part::slotDoFileDirty()
{
bool tocReloadPrepared = false;
// do the following the first time the file is reloaded
if ( m_viewportDirty.pageNumber == -1 )
{
@ -1725,7 +1726,7 @@ void Part::slotDoFileDirty()
// store if presentation view was open
m_wasPresentationOpen = ((PresentationWidget*)m_presentationWidget != 0);
// preserves the toc state after reload
m_toc->prepareForReload();
tocReloadPrepared = true;
@ -1743,13 +1744,13 @@ void Part::slotDoFileDirty()
{
m_viewportDirty.pageNumber = -1;
if ( tocReloadPrepared )
if ( tocReloadPrepared )
{
m_toc->rollbackReload();
}
return;
}
if ( tocReloadPrepared )
m_toc->finishReload();
@ -1783,7 +1784,7 @@ void Part::slotDoFileDirty()
}
else
{
// start watching the file again (since we dropped it on close)
// start watching the file again (since we dropped it on close)
addFileToWatcher( m_watcher, localFilePath() );
m_dirtyHandler->start( 750 );
}
@ -1796,14 +1797,14 @@ void Part::updateViewActions()
if ( opened )
{
m_gotoPage->setEnabled( m_document->pages() > 1 );
// Check if you are at the beginning or not
if (m_document->currentPage() != 0)
{
m_beginningOfDocument->setEnabled( true );
m_prevPage->setEnabled( true );
}
else
else
{
if (m_pageView->verticalScrollBar()->value() != 0)
{
@ -1818,7 +1819,7 @@ void Part::updateViewActions()
// The document is at the first page, you can go to a page before
m_prevPage->setEnabled( false );
}
if (m_document->pages() == m_document->currentPage() + 1 )
{
// If you are at the end, disable go to next page
@ -1828,13 +1829,13 @@ void Part::updateViewActions()
// If you are the end of the page of the last document, you can't go to the last page
m_endOfDocument->setEnabled( false );
}
else
else
{
// Otherwise you can move to the endif
m_endOfDocument->setEnabled( true );
}
}
else
else
{
// If you are not at the end, enable go to next page
m_nextPage->setEnabled( true );
@ -2936,7 +2937,7 @@ void Part::rebuildBookmarkMenu( bool unplugActions )
m_bookmarkActions.append( a );
}
plugActionList( "bookmarks_currentdocument", m_bookmarkActions );
if (factory())
{
const QList<KXMLGUIClient*> clients(factory()->clients());

@ -70,7 +70,9 @@
#include "pageviewannotator.h"
#include "priorities.h"
#include "toolaction.h"
//#include "tts.h"
#ifdef HAVE_SPEECH
#include "tts.h"
#endif
#include "videowidget.h"
#include "core/action.h"
#include "core/area.h"
@ -119,7 +121,9 @@ public:
PageViewPrivate( PageView *qq );
FormWidgetsController* formWidgetsController();
// OkularTTS* tts();
#ifdef HAVE_SPEECH
OkularTTS* tts();
#endif
QString selectedText() const;
// the document, pageviewItems and the 'visible cache'
@ -176,7 +180,9 @@ public:
PageViewMessage * messageWindow; // in pageviewutils.h
bool m_formsVisible;
FormWidgetsController *formsWidgetController;
// OkularTTS * m_tts;
#ifdef HAVE_SPEECH
OkularTTS * m_tts;
#endif
QTimer * refreshTimer;
int refreshPage;
@ -227,6 +233,9 @@ public:
PageViewPrivate::PageViewPrivate( PageView *qq )
: q( qq )
#ifdef HAVE_SPEECH
, m_tts( 0 )
#endif
{
}
@ -244,22 +253,22 @@ FormWidgetsController* PageViewPrivate::formWidgetsController()
return formsWidgetController;
}
//OkularTTS* PageViewPrivate::tts()
//{
// if ( !m_tts )
// {
// m_tts = new OkularTTS( q );
// if ( aSpeakStop )
// {
// QObject::connect( m_tts, SIGNAL(hasSpeechs(bool)),
// aSpeakStop, SLOT(setEnabled(bool)) );
// QObject::connect( m_tts, SIGNAL(errorMessage(QString)),
// q, SLOT(errorMessage(QString)) );
// }
// }
#ifdef HAVE_SPEECH
OkularTTS* PageViewPrivate::tts()
{
if ( !m_tts )
{
m_tts = new OkularTTS( q );
if ( aSpeakStop )
{
QObject::connect( m_tts, SIGNAL(isSpeaking(bool)),
aSpeakStop, SLOT(setEnabled(bool)) );
}
}
// return m_tts;
//}
return m_tts;
}
#endif
/* PageView. What's in this file? -> quick overview.
@ -305,7 +314,9 @@ PageView::PageView( QWidget *parent, Okular::Document *document )
d->messageWindow = new PageViewMessage(this);
d->m_formsVisible = false;
d->formsWidgetController = 0;
// d->m_tts = 0;
#ifdef HAVE_SPEECH
d->m_tts = 0;
#endif
d->refreshTimer = 0;
d->refreshPage = -1;
d->aRotateClockwise = 0;
@ -410,8 +421,10 @@ PageView::PageView( QWidget *parent, Okular::Document *document )
PageView::~PageView()
{
// if ( d->m_tts )
// d->m_tts->stopAllSpeechs();
#ifdef HAVE_SPEECH
if ( d->m_tts )
d->m_tts->stopAllSpeechs();
#endif
// delete the local storage structure
@ -1074,12 +1087,14 @@ void PageView::updateActionState( bool haspages, bool documentChanged, bool hasf
}
d->aToggleAnnotator->setEnabled( allowAnnotations );
}
#ifdef HAVE_SPEECH
if ( d->aSpeakDoc )
{
const bool enablettsactions = haspages ? Okular::Settings::useKTTSD() : false;
const bool enablettsactions = haspages ? Okular::Settings::useTTS() : false;
d->aSpeakDoc->setEnabled( enablettsactions );
d->aSpeakPage->setEnabled( enablettsactions );
}
#endif
if (d->aMouseMagnifier)
d->aMouseMagnifier->setEnabled(d->document->supportsTiles());
}
@ -2600,7 +2615,7 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
textToClipboard->setEnabled( false );
textToClipboard->setText( i18n("Copy forbidden by DRM") );
}
if ( Okular::Settings::useKTTSD() )
if ( Okular::Settings::useTTS() )
speakText = menu.addAction( QIcon::fromTheme("text-speak"), i18n( "Speak Text" ) );
if ( copyAllowed )
{
@ -2665,11 +2680,13 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
if ( cb->supportsSelection() )
cb->setText( selectedText, QClipboard::Selection );
}
#ifdef HAVE_SPEECH
else if ( choice == speakText )
{
// [2] speech selection using KTTSD
// d->tts()->say( selectedText );
// [2] speech selection using TTS
d->tts()->say( selectedText );
}
#endif
}
}
// clear widget selection and invalidate rect
@ -2850,10 +2867,10 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
{
QMenu menu( this );
QAction *textToClipboard = menu.addAction( QIcon::fromTheme( "edit-copy" ), i18n( "Copy Text" ) );
QAction *speakText = 0;
QAction *httpLink = 0;
// if ( Okular::Settings::useKTTSD() )
// speakText = menu.addAction( QIcon::fromTheme( "text-speak" ), i18n( "Speak Text" ) );
QAction *speakText = 0;
if ( Okular::Settings::useTTS() )
speakText = menu.addAction( QIcon::fromTheme( "text-speak" ), i18n( "Speak Text" ) );
if ( !d->document->isAllowed( Okular::AllowCopy ) )
{
textToClipboard->setEnabled( false );
@ -2875,11 +2892,13 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
{
if ( choice == textToClipboard )
copyTextSelection();
#ifdef HAVE_SPEECH
else if ( choice == speakText )
{
const QString text = d->selectedText();
// d->tts()->say( text );
d->tts()->say( text );
}
#endif
else if ( choice == httpLink )
new KRun( QUrl( url ), this );
}
@ -4938,40 +4957,42 @@ void PageView::slotRefreshPage()
Q_ARG( int, req ) );
}
#ifdef HAVE_SPEECH
void PageView::slotSpeakDocument()
{
// QString text;
// QVector< PageViewItem * >::const_iterator it = d->items.constBegin(), itEnd = d->items.constEnd();
// for ( ; it < itEnd; ++it )
// {
// Okular::RegularAreaRect * area = textSelectionForItem( *it );
// text.append( (*it)->page()->text( area ) );
// text.append( '\n' );
// delete area;
// }
QString text;
QVector< PageViewItem * >::const_iterator it = d->items.constBegin(), itEnd = d->items.constEnd();
for ( ; it < itEnd; ++it )
{
Okular::RegularAreaRect * area = textSelectionForItem( *it );
text.append( (*it)->page()->text( area ) );
text.append( '\n' );
delete area;
}
// d->tts()->say( text );
d->tts()->say( text );
}
void PageView::slotSpeakCurrentPage()
{
// const int currentPage = d->document->viewport().pageNumber;
const int currentPage = d->document->viewport().pageNumber;
// PageViewItem *item = d->items.at( currentPage );
// Okular::RegularAreaRect * area = textSelectionForItem( item );
// const QString text = item->page()->text( area );
// delete area;
PageViewItem *item = d->items.at( currentPage );
Okular::RegularAreaRect * area = textSelectionForItem( item );
const QString text = item->page()->text( area );
delete area;
// d->tts()->say( text );
d->tts()->say( text );
}
void PageView::slotStopSpeaks()
{
// if ( !d->m_tts )
// return;
if ( !d->m_tts )
return;
// d->m_tts->stopAllSpeechs();
d->m_tts->stopAllSpeechs();
}
#endif
void PageView::slotAction( Okular::Action *action )
{

@ -238,9 +238,11 @@ Q_OBJECT
void slotToggleForms();
void slotFormChanged( int pageNumber );
void slotRefreshPage();
#ifdef HAVE_SPEECH
void slotSpeakDocument();
void slotSpeakCurrentPage();
void slotStopSpeaks();
#endif
void slotAction( Okular::Action *action );
void externalKeyPressEvent( QKeyEvent *e );
void slotAnnotationWindowDestroyed( QObject *window );

@ -12,80 +12,35 @@
#include <qdbusservicewatcher.h>
#include <qset.h>
#include <KLocalisedString>
#include <kspeech.h>
#include <ktoolinvocation.h>
#include "kspeechinterface.h"
#include <KLocalizedString>
/* Private storage. */
class OkularTTS::Private
{
public:
Private( OkularTTS *qq )
: q( qq ), kspeech( 0 )
, watcher( "org.kde.kttsd", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration )
: q( qq ), speech( new QTextToSpeech )
{
}
void setupIface();
void teardownIface();
OkularTTS *q;
org::kde::KSpeech* kspeech;
QSet< int > jobs;
QDBusServiceWatcher watcher;
};
void OkularTTS::Private::setupIface()
{
if ( kspeech )
return;
// If KTTSD not running, start it.
QDBusReply<bool> reply = QDBusConnection::sessionBus().interface()->isServiceRegistered( "org.kde.kttsd" );
bool kttsdactive = false;
if ( reply.isValid() )
kttsdactive = reply.value();
if ( !kttsdactive )
~Private()
{
QString error;
if ( KToolInvocation::startServiceByDesktopName( "kttsd", QStringList(), &error ) )
{
emit q->errorMessage( i18n( "Starting Jovie Text-to-Speech service Failed: %1", error ) );
}
else
{
kttsdactive = true;
}
delete speech;
speech = 0;
}
if ( kttsdactive )
{
// creating the connection to the kspeech interface
kspeech = new org::kde::KSpeech( "org.kde.kttsd", "/KSpeech", QDBusConnection::sessionBus() );
kspeech->setParent( q );
kspeech->setApplicationName( "Okular" );
connect(kspeech, &org::kde::KSpeech::jobStateChanged, q, &OkularTTS::slotJobStateChanged);
}
}
void OkularTTS::Private::teardownIface()
{
delete kspeech;
kspeech = 0;
}
OkularTTS *q;
QTextToSpeech *speech;
};
OkularTTS::OkularTTS( QObject *parent )
: QObject( parent ), d( new Private( this ) )
{
connect(&d->watcher, &QDBusServiceWatcher::serviceUnregistered, this, &OkularTTS::slotServiceUnregistered);
connect( d->speech, &QTextToSpeech::stateChanged, this, &OkularTTS::slotSpeechStateChanged);
}
OkularTTS::~OkularTTS()
{
disconnect( &d->watcher, 0, this, 0 );
delete d;
}
@ -94,50 +49,24 @@ void OkularTTS::say( const QString &text )
if ( text.isEmpty() )
return;
d->setupIface();
if ( d->kspeech )
{
QDBusReply< int > reply = d->kspeech->say( text, KSpeech::soPlainText );
if ( reply.isValid() )
{
d->jobs.insert( reply.value() );
emit hasSpeechs( true );
}
}
d->speech->say( text );
}
void OkularTTS::stopAllSpeechs()
{
if ( !d->kspeech )
if ( !d->speech )
return;
d->kspeech->removeAllJobs();
}
void OkularTTS::slotServiceUnregistered( const QString &service )
{
if ( service == QLatin1String( "org.kde.kttsd" ) )
{
d->teardownIface();
}
d->speech->stop();
}
void OkularTTS::slotJobStateChanged( const QString &appId, int jobNum, int state )
void OkularTTS::slotSpeechStateChanged(QTextToSpeech::State state)
{
// discard non ours job
if ( appId != QDBusConnection::sessionBus().baseService() || !d->kspeech )
return;
switch ( state )
{
case KSpeech::jsDeleted:
d->jobs.remove( jobNum );
emit hasSpeechs( !d->jobs.isEmpty() );
break;
case KSpeech::jsFinished:
d->kspeech->removeJob( jobNum );
break;
}
if (state == QTextToSpeech::Speaking)
emit isSpeaking(true);
else
emit isSpeaking(false);
}
#include "moc_tts.cpp"

@ -11,6 +11,7 @@
#define _TTS_H_
#include <qobject.h>
#include <QTextToSpeech>
class OkularTTS : public QObject
{
@ -22,13 +23,11 @@ class OkularTTS : public QObject
void say( const QString &text );
void stopAllSpeechs();
signals:
void hasSpeechs( bool has );
void errorMessage( const QString &message );
public slots:
void slotSpeechStateChanged(QTextToSpeech::State state);
private slots:
void slotServiceUnregistered( const QString& );
void slotJobStateChanged( const QString &appId, int jobNum, int state );
signals:
void isSpeaking( bool speaking );
private:
// private storage

Loading…
Cancel
Save