diff --git a/.travis.yml b/.travis.yml index bd4bbc8c458..e81c923172b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ addons: apt: sources: &add-sources - sourceline: 'ppa:beineri/opt-qt571-trusty' - - sourceline: 'ppa:beineri/opt-qt593-trusty' + - sourceline: 'ppa:beineri/opt-qt-5.10.1-trusty' - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:ondrej/php' packages: &common-packages @@ -56,49 +56,50 @@ matrix: compiler: gcc env: - Q_OR_C_MAKE=qmake - QT_VERSION=59 + QT_VERSION=510 addons: apt: sources: *add-sources - packages: &qt59-packages + packages: &qt510-packages - *common-packages - - qt59base - - qt59multimedia - - qt59tools - - qt59gamepad + - qt510base + - qt510multimedia + - qt510tools + - qt510gamepad + - qt510speech - os: linux compiler: gcc env: - Q_OR_C_MAKE=cmake - QT_VERSION=59 + QT_VERSION=510 addons: apt: sources: *add-sources packages: - - *qt59-packages + - *qt510-packages - os: linux compiler: clang env: - Q_OR_C_MAKE=qmake - QT_VERSION=59 + QT_VERSION=510 addons: apt: sources: *add-sources packages: - - *qt59-packages + - *qt510-packages - os: linux compiler: clang env: - Q_OR_C_MAKE=cmake - QT_VERSION=59 + QT_VERSION=510 addons: apt: sources: *add-sources packages: - - *qt59-packages + - *qt510-packages - os: linux compiler: gcc diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f413c6518a..6e94cec4567 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,12 +124,12 @@ endif() # something else or not even exist). set(FONT_TEST $ENV{WITH_FONTS}) if(DEFINED FONT_TEST) - string(TOUPPER ${FONT_TEST} FONT_TEST) + string(TOUPPER ${FONT_TEST} FONT_TEST) else() - # Set the CMake variable to the default if it (as a result of WITH_FONTS not - # existing) has not been defined, without the if(DEFINED ...) string(...) - # errors out - and so may further tests on the variable... - set(FONT_TEST "YES") + # Set the CMake variable to the default if it (as a result of WITH_FONTS not + # existing) has not been defined, without the if(DEFINED ...) string(...) + # errors out - and so may further tests on the variable... + set(FONT_TEST "YES") endif() if(FONT_TEST STREQUAL "NO") option(USE_FONTS "Include Deja Vu and Ubuntu fonts in the executable" OFF) @@ -214,10 +214,10 @@ endif() file(GLOB_RECURSE lua_files RELATIVE "${CMAKE_HOME_DIRECTORY}/src/mudlet-lua/lua/" "${CMAKE_HOME_DIRECTORY}/src/mudlet-lua/lua/*.lua") list(LENGTH lua_files lua_file_count) -if(lua_file_count EQUAL 25) +if(lua_file_count EQUAL 26) message(STATUS "Found Mudlet & Geyser lua files") else() - message(WARNING "Found ${lua_file_count} Mudlet & Geyser lua files but 25 were expected:") + message(WARNING "Found ${lua_file_count} Mudlet & Geyser lua files but 26 were expected:") foreach(lua_file ${lua_files}) message(STATUS " ${lua_file}") endforeach(lua_file) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5f625dc6486..e7414305f30 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,8 +22,8 @@ set(mudlet_RCCS mudlet.qrc) if(USE_FONTS) - set(mudlet_fonts_RCCS mudlet_fonts.qrc) - add_definitions(-DINCLUDE_FONTS) + set(mudlet_fonts_RCCS mudlet_fonts.qrc) + add_definitions(-DINCLUDE_FONTS) endif() set(mudlet_SRCS @@ -102,29 +102,28 @@ set(mudlet_SRCS # This is for compiled UI files, not those used at runtime though the resource file. set(mudlet_UIS - ui/about_dialog.ui - ui/actions_main_area.ui - ui/aliases_main_area.ui - ui/color_trigger.ui - ui/composer.ui - ui/connection_profiles.ui - ui/dlgPackageExporter.ui - ui/irc.ui - ui/keybindings_main_area.ui - ui/main_window.ui - ui/mapper.ui - ui/notes_editor.ui - ui/profile_preferences.ui - ui/room_exits.ui - ui/scripts_main_area.ui - ui/source_editor_area.ui - ui/system_message_area.ui - ui/timers_main_area.ui - ui/triggers_main_area.ui - ui/trigger_editor.ui - ui/trigger_pattern_edit.ui - ui/vars_main_area.ui -) + ui/about_dialog.ui + ui/actions_main_area.ui + ui/aliases_main_area.ui + ui/color_trigger.ui + ui/composer.ui + ui/connection_profiles.ui + ui/dlgPackageExporter.ui + ui/irc.ui + ui/keybindings_main_area.ui + ui/main_window.ui + ui/mapper.ui + ui/notes_editor.ui + ui/profile_preferences.ui + ui/room_exits.ui + ui/scripts_main_area.ui + ui/source_editor_area.ui + ui/system_message_area.ui + ui/timers_main_area.ui + ui/triggers_main_area.ui + ui/trigger_editor.ui + ui/trigger_pattern_edit.ui + ui/vars_main_area.ui) set(mudlet_MOC_HDRS ctelnet.h @@ -341,10 +340,23 @@ find_package(Qt5 5.7 REQUIRED COMPONENTS Core Multimedia Network OpenGL UiTools # This is not present before Qt 5.7: find_package(Qt5 COMPONENTS Gamepad QUIET) +# This is not present before Qt 5.9: +find_package(Qt5 COMPONENTS TextToSpeech QUIET) + if (Qt5Core_VERSION VERSION_LESS 5.7) message(FATAL_ERROR "Mudlet requires Qt 5.7 or later, yours is ${Qt5Core_VERSION}") +elif(Qt5Core_VERSION VERSION_LESS 5.9) + # Qt Gamepad was a Technology Preview in 5.7 + # Qt Gamepad was a Technology Preview 2 in 5.8 + # Qt Speech (or at least the TTS part) was a Technology Preview in 5.8 + message(WARNING "Qt version: ${Qt5Core_VERSION} - Gamepad and TextToSpeech may not be supported, as officially they requires 5.9 and 5.10 respectively") +elif(Qt5Core_VERSION VERSION_LESS 5.10) + # Qt Speech (or at least the TTS part) was a Technology Preview 2 in 5.9, now supported on Mingw + # Qt Gamepad was officially released in 5.9 + message(WARNING "Qt version: ${Qt5Core_VERSION} - TextToSpeech may not be supported, officially it requires 5.10") else() - message(STATUS "Qt version: ${Qt5Core_VERSION}") + # Qt Speech (or at least the TTS part) was officially release in 5.10 + message(STATUS "Qt version: ${Qt5Core_VERSION}") endif() message(STATUS "Using ${CMAKE_CXX_COMPILER_ID} compiler") @@ -397,8 +409,8 @@ find_package(Boost 1.44 COMPONENTS graph) add_definitions(-DAPP_VERSION="${APP_VERSION}" -DAPP_BUILD="${APP_BUILD}" -DAPP_TARGET="${APP_TARGET}") if(UNIX) - set(LUA_DEFAULT_DIR "${CMAKE_INSTALL_PREFIX}/share/mudlet/lua") - set(LCF_DIR "${CMAKE_INSTALL_PREFIX}/share/mudlet/lua/lcf") + set(LUA_DEFAULT_DIR "${CMAKE_INSTALL_PREFIX}/share/mudlet/lua") + set(LCF_DIR "${CMAKE_INSTALL_PREFIX}/share/mudlet/lua/lcf") endif(UNIX) # Define a preprocessor symbol with the default fallback location from which @@ -427,17 +439,23 @@ include_directories( ${PUGIXML_INCLUDE_DIR} ) +if(Qt5TextToSpeech_FOUND) + add_definitions(${Qt5TextToSpeech_DEFINITIONS}) + include_directories(${Qt5TextToSpeech_INCLUDE_DIRS}) + message(STATUS "Using TextToSpeech module") +endif(Qt5TextToSpeech_FOUND) + if(Qt5Gamepad_FOUND) - add_definitions(${Qt5Gamepad_DEFINITIONS}) - include_directories(${Qt5Gamepad_INCLUDE_DIRS}) - message(STATUS "Using Gamepad module") + add_definitions(${Qt5Gamepad_DEFINITIONS}) + include_directories(${Qt5Gamepad_INCLUDE_DIRS}) + message(STATUS "Using Gamepad module") endif(Qt5Gamepad_FOUND) # Need to use the plural variables as there can be more than ONE directory to specify: if(PC_ZIP_FOUND) - include_directories( ${PC_ZIP_INCLUDE_DIRS} ) + include_directories( ${PC_ZIP_INCLUDE_DIRS} ) else() - include_directories( ${ZIP_INCLUDE_DIRS} ) + include_directories( ${ZIP_INCLUDE_DIRS} ) endif() include_directories( ${ZLIB_INCLUDE_DIR} ) @@ -445,31 +463,29 @@ include_directories( ${ZLIB_INCLUDE_DIR} ) QT5_WRAP_UI(mudlet_UIS_H ${mudlet_UIS}) QT5_WRAP_CPP(mudlet_MOC_SRCS ${mudlet_MOC_HDRS}) if(USE_FONTS) - QT5_ADD_RESOURCES(mudlet_RCC_SRCS ${mudlet_RCCS} ${mudlet_fonts_RCCS}) + QT5_ADD_RESOURCES(mudlet_RCC_SRCS ${mudlet_RCCS} ${mudlet_fonts_RCCS}) else() - QT5_ADD_RESOURCES(mudlet_RCC_SRCS ${mudlet_RCCS}) + QT5_ADD_RESOURCES(mudlet_RCC_SRCS ${mudlet_RCCS}) endif() if(WIN32) - add_executable(mudlet WIN32 - ${mudlet_SRCS} - ${mudlet_MOC_SRCS} - ${mudlet_RCC_SRCS} - ${mudlet_HDRS} - ${mudlet_UIS_H} - ${mudlet_MOC_HDRS} - ${mudlet_LUA_FILES} - ) + add_executable(mudlet WIN32 + ${mudlet_SRCS} + ${mudlet_MOC_SRCS} + ${mudlet_RCC_SRCS} + ${mudlet_HDRS} + ${mudlet_UIS_H} + ${mudlet_MOC_HDRS} + ${mudlet_LUA_FILES}) else() - add_executable(mudlet - ${mudlet_SRCS} - ${mudlet_MOC_SRCS} - ${mudlet_RCC_SRCS} - ${mudlet_HDRS} - ${mudlet_UIS_H} - ${mudlet_MOC_HDRS} - ${mudlet_LUA_FILES} - ) + add_executable(mudlet + ${mudlet_SRCS} + ${mudlet_MOC_SRCS} + ${mudlet_RCC_SRCS} + ${mudlet_HDRS} + ${mudlet_UIS_H} + ${mudlet_MOC_HDRS} + ${mudlet_LUA_FILES}) endif() target_link_libraries(mudlet @@ -491,19 +507,22 @@ target_link_libraries(mudlet edbee-lib ) +if(Qt5TextToSpeech_FOUND) + target_link_libraries(mudlet ${Qt5TextToSpeech_LIBRARIES}) +endif(Qt5TextToSpeech_FOUND) + if(USE_UPDATER) - target_link_libraries(mudlet - dblsqd) + target_link_libraries(mudlet dblsqd) endif(USE_UPDATER) if(Qt5Gamepad_FOUND) - target_link_libraries(mudlet ${Qt5Gamepad_LIBRARIES}) + target_link_libraries(mudlet ${Qt5Gamepad_LIBRARIES}) endif(Qt5Gamepad_FOUND) if(PC_ZIP_FOUND) - target_link_libraries(mudlet ${PC_ZIP_LIBRARIES}) + target_link_libraries(mudlet ${PC_ZIP_LIBRARIES}) else() - target_link_libraries(mudlet ${ZIP_LIBRARIES}) + target_link_libraries(mudlet ${ZIP_LIBRARIES}) endif() if(WIN32) @@ -512,16 +531,14 @@ if(WIN32) endif() if(APPLE) - target_link_libraries(mudlet - luazip - ) + target_link_libraries(mudlet luazip) if(USE_UPDATER) target_link_libraries(mudlet - cocoa-qt-glue - "-framework Sparkle" - "-framework AppKit" - "-F${CMAKE_HOME_DIRECTORY}/3rdparty/cocoapods/Pods/Sparkle") + cocoa-qt-glue + "-framework Sparkle" + "-framework AppKit" + "-F${CMAKE_HOME_DIRECTORY}/3rdparty/cocoapods/Pods/Sparkle") endif(USE_UPDATER) endif() diff --git a/src/HostManager.cpp b/src/HostManager.cpp index e85a7123878..2000d4482d7 100644 --- a/src/HostManager.cpp +++ b/src/HostManager.cpp @@ -128,9 +128,11 @@ void HostManager::postIrcMessage(QString a, QString b, QString c) // send out the events to the other hosts in a predictable and consistent order // and so that no one host gets an unfair advantage when emitting events. The // sending profile host does NOT get the event! -void HostManager::postInterHostEvent(const Host* pHost, const TEvent& event) +// Note: Optional forceGlobal allows passing a null pointer as pHost, and will raise +// an event for all profiles. +void HostManager::postInterHostEvent(const Host* pHost, const TEvent& event, const bool forceGlobal) { - if (!pHost) { + if (!pHost && !forceGlobal) { return; } diff --git a/src/HostManager.h b/src/HostManager.h index 19f3a00dc67..50e3077ff17 100644 --- a/src/HostManager.h +++ b/src/HostManager.h @@ -48,7 +48,7 @@ class HostManager bool addHost(QString name, QString port, QString login, QString pass); bool deleteHost(QString); void postIrcMessage(QString, QString, QString); - void postInterHostEvent(const Host*, const TEvent&); + void postInterHostEvent(const Host*, const TEvent&, const bool = false); private: QReadWriteLock mPoolReadWriteLock; // Was QMutex, but we needed to allow concurrent read access diff --git a/src/TLuaInterpreter.cpp b/src/TLuaInterpreter.cpp index 205ae6640da..93b8aaed2ad 100644 --- a/src/TLuaInterpreter.cpp +++ b/src/TLuaInterpreter.cpp @@ -49,7 +49,10 @@ #include "pre_guard.h" #include #include -#include +#include +#ifdef QT_TEXTTOSPEECH_LIB +#include +#endif // QT_TEXTTOSPEECH_LIB #include "post_guard.h" #include @@ -85,6 +88,15 @@ int luaopen_yajl(lua_State*); using namespace std; +#ifdef QT_TEXTTOSPEECH_LIB +QPointer speechUnit; +QVector speechQueue; +bool bSpeechBuilt; +bool bSpeechQueueing; +int speechState = QTextToSpeech::Ready; +QString speechCurrent; +#endif // QT_TEXTTOSPEECH_LIB + TLuaInterpreter::TLuaInterpreter(Host* pH, int id) : mpHost(pH), mHostID(id), purgeTimer(this) { pGlobalLua = nullptr; @@ -5372,11 +5384,11 @@ int TLuaInterpreter::setPopup(lua_State* L) s++; } /* N/U: - if( n >= s ) - { - customFormat = lua_toboolean( L, s ); - } - */ + if( n >= s ) + { + customFormat = lua_toboolean( L, s ); + } + */ Host& host = getHostFromLua(L); QString txt = a2.c_str(); QString name = a1.c_str(); @@ -11999,6 +12011,524 @@ int TLuaInterpreter::restartIrc(lua_State* L) return 1; } +#ifdef QT_TEXTTOSPEECH_LIB + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSpeak +int TLuaInterpreter::ttsSpeak(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + if (!lua_isstring(L, 1)) { + lua_pushfstring(L, "ttsSpeak: bad argument #%1 type (text to say as string expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } + + QString textToSay; + textToSay = QString::fromUtf8(lua_tostring(L, 1)); + + speechUnit->say(textToSay); + speechCurrent = textToSay; + + return 0; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsBuild +void TLuaInterpreter::ttsBuild() +{ + if (bSpeechBuilt) { + return; + } + + speechUnit = new QTextToSpeech(); + + bSpeechBuilt = true; + bSpeechQueueing = false; + + connect(speechUnit, &QTextToSpeech::stateChanged, &TLuaInterpreter::ttsStateChanged); + + + speechUnit->setVolume(1.0); + speechUnit->setRate(0.0); + speechUnit->setPitch(0.0); + + return; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSkip +int TLuaInterpreter::ttsSkip(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + speechUnit->stop(); + + return 0; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetRate +int TLuaInterpreter::ttsSetRate(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + double rate; + if (!lua_isnumber(L, 1)) { + lua_pushfstring(L, "ttsSetRate: bad argument #1 type (rate as number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } else { + rate = lua_tonumber(L, 1); + } + + if (rate > 1.0) { + rate = 1.0; + } + + if (rate < -1.0) { + rate = -1.0; + } + + speechUnit->setRate(rate); + + TEvent event; + event.mArgumentList.append(QLatin1String("ttsRateChanged")); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(QString::number(rate)); + event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); + mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true); + + return 0; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetPitch +int TLuaInterpreter::ttsSetPitch(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + double pitch; + if (!lua_isnumber(L, 1)) { + lua_pushfstring(L, "ttsSetPitch: bad argument #1 type (pitch as number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } else { + pitch = lua_tonumber(L, 1); + } + + if (pitch > 1.0) { + pitch = 1.0; + } + + if (pitch < -1.0) { + pitch = -1.0; + } + + speechUnit->setPitch(pitch); + + TEvent event; + event.mArgumentList.append(QLatin1String("ttsPitchChanged")); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(QString::number(pitch)); + event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); + mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true); + + return 0; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetVolume +int TLuaInterpreter::ttsSetVolume(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + double volume; + if (!lua_isnumber(L, 1)) { + lua_pushfstring(L, "ttsSetVolume: bad argument #1 type (volume as number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } else { + volume = lua_tonumber(L, 1); + } + + if (volume > 1.0) { + volume = 1.0; + } + + if (volume < 0.0) { + volume = 0.0; + } + + speechUnit->setVolume(volume); + + TEvent event; + event.mArgumentList.append(QLatin1String("ttsVolumeChanged")); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(QString::number(volume)); + event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); + mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true); + + return 0; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetVolume +int TLuaInterpreter::ttsGetVolume(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + lua_pushnumber(L, speechUnit->volume()); + return 1; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetRate +int TLuaInterpreter::ttsGetRate(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + lua_pushnumber(L, speechUnit->rate()); + return 1; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetPitch +int TLuaInterpreter::ttsGetPitch(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + lua_pushnumber(L, speechUnit->pitch()); + return 1; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetVoices +int TLuaInterpreter::ttsGetVoices(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + QVector speechVoices = speechUnit->availableVoices(); + int i = 0; + lua_newtable(L); + for (const QVoice& voice : speechVoices) { + lua_pushnumber(L, ++i); + lua_pushstring(L, voice.name().toUtf8().constData()); + lua_settable(L, -3); + } + + return 1; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetCurrentVoice +int TLuaInterpreter::ttsGetCurrentVoice(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + QString currentVoice = speechUnit->voice().name(); + lua_pushstring(L, currentVoice.toUtf8().constData()); + return 1; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetVoiceByName +int TLuaInterpreter::ttsSetVoiceByName(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + QString nextVoice; + + if (!lua_isstring(L, 1)) { + lua_pushfstring(L, "ttsSetVoiceByName: bad argument #1 type (voice as string expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } else { + nextVoice = QString(lua_tostring(L, 1)); + } + + QVector speechVoices = speechUnit->availableVoices(); + foreach (const QVoice& voice, speechVoices) { + if (voice.name() == nextVoice) { + speechUnit->setVoice(voice); + lua_pushboolean(L, true); + + TEvent event; + event.mArgumentList.append(QLatin1String("ttsVoiceChanged")); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(voice.name()); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true); + + return 1; + } + } + + lua_pushboolean(L, false); + return 1; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsSetVoiceByIndex +int TLuaInterpreter::ttsSetVoiceByIndex(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + int index; + if (!lua_isnumber(L, 1)) { + lua_pushfstring(L, "ttsSetVoiceByIndex: bad argument #1 type (voice as index number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } else { + index = lua_tonumber(L, 1); + } + + index--; + + QVector speechVoices = speechUnit->availableVoices(); + if (index < 0 || index >= speechVoices.size()) { + lua_pushboolean(L, false); + return 1; + } + + speechUnit->setVoice(speechVoices.at(index)); + + TEvent event; + event.mArgumentList.append(QLatin1String("ttsVoiceChanged")); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(speechVoices[index].name()); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true); + + lua_pushboolean(L, true); + return 1; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsStateChanged +void TLuaInterpreter::ttsStateChanged(QTextToSpeech::State state) +{ + if (state != speechState) { + speechState = state; + TEvent event; + switch (state) { + case QTextToSpeech::Paused: + event.mArgumentList.append(QLatin1String("ttsSpeechPaused")); + break; + case QTextToSpeech::Speaking: + event.mArgumentList.append(QLatin1String("ttsSpeechStarted")); + break; + case QTextToSpeech::BackendError: + event.mArgumentList.append(QLatin1String("ttsSpeechError")); + break; + case QTextToSpeech::Ready: + event.mArgumentList.append(QLatin1String("ttsSpeechReady")); + break; + } + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + + if (state == QTextToSpeech::Speaking) { + event.mArgumentList.append(speechCurrent); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + } + + mudlet::self()->getHostManager().postInterHostEvent(NULL, event, true); + } + + if (state != QTextToSpeech::Ready || speechQueue.empty()) { + bSpeechQueueing = false; + return; + } + + QString textToSay; + textToSay = speechQueue.takeFirst(); + + speechUnit->say(textToSay); + speechCurrent = textToSay; + + return; +} + + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsQueue +int TLuaInterpreter::ttsQueue(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + if (!lua_isstring(L, 1)) { + lua_pushfstring(L, "ttsQueueText: bad argument #1 type (input as string expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } + + QString inputText = lua_tostring(L, 1); + int index; + + if (lua_gettop(L) > 1) { + if (!lua_isnumber(L, 2)) { + lua_pushfstring(L, "ttsQueueText: bad argument #2 type (index as number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } + + index = lua_tonumber(L, 2); + index--; + + if (index < 0) { + index = 0; + } + + if (index > speechQueue.size()) { + index = speechQueue.size(); + } + } else { + index = speechQueue.size(); + } + + speechQueue.insert(index, inputText); + + TEvent event; + Host& host = getHostFromLua(L); + event.mArgumentList.append(QLatin1String("ttsSpeechQueued")); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(inputText); + event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); + event.mArgumentList.append(QString::number(index)); + event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); + host.raiseEvent(event); + + if (speechQueue.size() == 1 && speechUnit->state() == QTextToSpeech::Ready && bSpeechQueueing == false) { + bSpeechQueueing = true; + TLuaInterpreter::ttsStateChanged(speechUnit->state()); + } + + return 0; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetQueue +int TLuaInterpreter::ttsGetQueue(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + if (lua_gettop(L) > 0) { + if (!lua_isnumber(L, 1)) { + lua_pushfstring(L, "ttsGetQueue: bad argument #1 type (index as number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } + + int index = lua_tonumber(L, 1); + index--; + + if (index < 0 || index > speechQueue.size()) { + lua_pushboolean(L, false); + return 1; + } + + lua_pushstring(L, speechQueue.at(index).toLatin1().constData()); + return 1; + } + + lua_newtable(L); + + for (int i = 0; i < speechQueue.size(); i++) { + lua_pushnumber(L, i + 1); + lua_pushstring(L, speechQueue.at(i).toUtf8().constData()); + lua_settable(L, -3); + } + + return 1; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsPause +int TLuaInterpreter::ttsPause(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + speechUnit->pause(); + + return 0; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsResume +int TLuaInterpreter::ttsResume(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + speechUnit->resume(); + + return 0; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsClearQueue +int TLuaInterpreter::ttsClearQueue(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + if (lua_gettop(L) > 0) { + if (!lua_isnumber(L, 1)) { + lua_pushfstring(L, "ttsClearQueue: bad argument #1 type (index as number expected, got %s!)", luaL_typename(L, 1)); + lua_error(L); + return 1; + } + + int index = lua_tonumber(L, 1); + index--; + + if (index < 0 || index >= speechQueue.size()) { + lua_pushnil(L); + lua_pushfstring(L, "index (%d) out of bounds for queue size %d", index + 1, speechQueue.size()); + return 2; + } + + speechQueue.remove(index); + return 0; + } + + speechQueue.clear(); + return 0; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetCurrentLine +int TLuaInterpreter::ttsGetCurrentLine(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + if (speechUnit->state() == QTextToSpeech::Ready) { + lua_pushnil(L); + lua_pushfstring(L, "not speaking any text"); + return 2; + } else if (speechUnit->state() == QTextToSpeech::BackendError) { + lua_pushnil(L); + lua_pushfstring(L, "error with the backend"); + return 2; + } + + lua_pushstring(L, speechCurrent.toUtf8().constData()); + return 1; +} + +// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#ttsGetState +int TLuaInterpreter::ttsGetState(lua_State* L) +{ + TLuaInterpreter::ttsBuild(); + + switch (speechUnit->state()) { + case QTextToSpeech::Ready: + lua_pushstring(L, "ttsSpeechReady"); + break; + case QTextToSpeech::Paused: + lua_pushstring(L, "ttsSpeechPaused"); + break; + case QTextToSpeech::Speaking: + lua_pushstring(L, "ttsSpeechStarted"); + break; + case QTextToSpeech::BackendError: + lua_pushstring(L, "ttsSpeechError"); + break; + default: + lua_pushstring(L, "ttsUnknownState"); + } + + return 1; +} + +#endif // QT_TEXTTOSPEECH_LIB + // Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#setServerEncoding int TLuaInterpreter::setServerEncoding(lua_State* L) { @@ -12298,17 +12828,17 @@ void TLuaInterpreter::setMultiCaptureGroups(const std::list >::const_iterator mit = mMultiCaptureGroupList.begin(); - int k=1; - for( ; mit!=mMultiCaptureGroupList.end(); mit++, k++ ) - { - cout << "regex#"<::const_iterator it = (*mit).begin(); - for( int i=1; it!=(*mit).end(); it++, i++ ) - { - cout << i<<"#"<<"<"<<*it<<">"<::const_iterator it = (*mit).begin(); + for( int i=1; it!=(*mit).end(); it++, i++ ) + { + cout << i<<"#"<<"<"<<*it<<">"<& captureList mCaptureGroupPosList = posList; /*std::list::iterator it2 = mCaptureGroupList.begin(); - std::list::iterator it1 = mCaptureGroupPosList.begin(); - int i=0; - for( ; it1!=mCaptureGroupPosList.end(); it1++, it2++, i++ ) - { - cout << "group#"<