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 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 find_package(KF5 REQUIRED COMPONENTS
Activities Activities
Archive Archive
@ -253,10 +260,14 @@ set(okularpart_SRCS
ui/toc.cpp ui/toc.cpp
ui/tocmodel.cpp ui/tocmodel.cpp
ui/toolaction.cpp ui/toolaction.cpp
# ui/tts.cpp
ui/videowidget.cpp ui/videowidget.cpp
) )
if (Qt5TextToSpeech_FOUND)
set(okularpart_SRCS ${okularpart_SRCS}
ui/tts.cpp)
endif()
ki18n_wrap_ui(okularpart_SRCS ki18n_wrap_ui(okularpart_SRCS
conf/dlgaccessibilitybase.ui conf/dlgaccessibilitybase.ui
conf/dlgeditorbase.ui conf/dlgeditorbase.ui
@ -268,7 +279,6 @@ ki18n_wrap_ui(okularpart_SRCS
kconfig_add_kcfg_files(okularpart_SRCS conf/settings.kcfgc ) 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) #qt5_add_dbus_interfaces(okularpart_SRCS ${KDE4_DBUS_INTERFACES_DIR}/org.kde.KSpeech.xml)
add_library(okularpart MODULE ${okularpart_SRCS}) 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) 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}) install(TARGETS okularpart DESTINATION ${PLUGIN_INSTALL_DIR})

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

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

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

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

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

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

Loading…
Cancel
Save