/****************************************************************************** * QSkinny - Copyright (C) 2016 Uwe Rathmann * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ #include "QskHunspellTextPredictor.h" #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) #include namespace { class StringConverter { public: StringConverter( const QByteArray& encoding ) : m_codec( QTextCodec::codecForName( encoding ) ) { } inline QString fromHunspell( const char* text ) const { if ( m_codec ) return m_codec->toUnicode( text ); return QString::fromUtf8( text ); } inline QByteArray toHunspell( const QString& text ) const { if ( m_codec ) return m_codec->fromUnicode( text ); return text.toUtf8(); } private: QTextCodec* m_codec; }; } #else #include namespace { class StringConverter { public: StringConverter( const QByteArray& encoding ) : m_decoder( encoding ) , m_encoder( encoding ) { } inline QString fromHunspell( const char* text ) const { if ( m_decoder.isValid() ) return m_decoder.decode( text ); return QString::fromUtf8( text ); } inline QByteArray toHunspell( const QString& text ) const { if ( m_encoder.isValid() ) return m_encoder.encode( text ); return text.toUtf8(); } private: mutable QStringDecoder m_decoder; mutable QStringEncoder m_encoder; }; } #endif class QskHunspellTextPredictor::PrivateData { public: Hunhandle* hunspellHandle = nullptr; QByteArray hunspellEncoding; QStringList candidates; QLocale locale; }; QskHunspellTextPredictor::QskHunspellTextPredictor( const QLocale& locale, QObject* object ) : Inherited( object ) , m_data( new PrivateData() ) { m_data->locale = locale; // make sure we call virtual functions: QMetaObject::invokeMethod( this, &QskHunspellTextPredictor::loadDictionaries, Qt::QueuedConnection ); } QskHunspellTextPredictor::~QskHunspellTextPredictor() { Hunspell_destroy( m_data->hunspellHandle ); } void QskHunspellTextPredictor::reset() { if ( !m_data->candidates.isEmpty() ) { m_data->candidates.clear(); Q_EMIT predictionChanged( QString(), {} ); } } QPair< QString, QString > QskHunspellTextPredictor::affAndDicFile( const QString& path, const QLocale& locale ) { QString prefix = QStringLiteral( "%1/%2" ).arg( path, locale.name() ); QString affFile = prefix + QStringLiteral( ".aff" ); QString dicFile = prefix + QStringLiteral( ".dic" ); if( QFile::exists( affFile ) && QFile::exists( dicFile ) ) { return qMakePair( affFile, dicFile ); } else { return {}; } } void QskHunspellTextPredictor::loadDictionaries() { const auto userPaths = QString::fromUtf8( qgetenv( "QSK_HUNSPELL_PATH" ) ); auto paths = userPaths.split( QDir::listSeparator(), Qt::SkipEmptyParts ); #if !defined( Q_OS_WIN32 ) paths += QStringLiteral( "/usr/share/hunspell" ); paths += QStringLiteral( "/usr/share/myspell/dicts" ); #endif for( const auto& path : paths ) { auto files = affAndDicFile( path, m_data->locale ); if( !files.first.isEmpty() && !files.second.isEmpty() ) { m_data->hunspellHandle = Hunspell_create( files.first.toUtf8(), files.second.toUtf8() ); m_data->hunspellEncoding = Hunspell_get_dic_encoding( m_data->hunspellHandle ); break; } } if( !m_data->hunspellHandle ) { qWarning() << "could not find Hunspell files for locale" << m_data->locale << "in the following directories:" << paths << ". Consider setting QSK_HUNSPELL_PATH to the directory " << "containing Hunspell .aff and .dic files."; } } void QskHunspellTextPredictor::request( const QString& text ) { if( !m_data->hunspellHandle ) { Q_EMIT predictionChanged( text, {} ); return; } StringConverter converter( m_data->hunspellEncoding ); char** suggestions; const int count = Hunspell_suggest( m_data->hunspellHandle, &suggestions, converter.toHunspell( text ).constData() ); QStringList candidates; candidates.reserve( count ); for ( int i = 0; i < count; i++ ) { const auto suggestion = converter.fromHunspell( suggestions[ i ] ); if ( suggestion.startsWith( text ) ) candidates.prepend( suggestion ); else candidates.append( suggestion ); } Hunspell_free_list( m_data->hunspellHandle, &suggestions, count ); m_data->candidates = candidates; Q_EMIT predictionChanged( text, m_data->candidates ); } #include "moc_QskHunspellTextPredictor.cpp"