diff --git a/locale/crowdin.ts b/locale/crowdin.ts index af0232202..c3eaa91e9 100644 --- a/locale/crowdin.ts +++ b/locale/crowdin.ts @@ -1,6 +1,6 @@ - + About @@ -60,14 +60,6 @@ ArticleMaker - - Expand article - - - - Collapse article - - No translation for <b dir="%3">%1</b> was found in group <b>%2</b>. @@ -95,18 +87,10 @@ ArticleRequest - - Expand article - - From - - Collapse article - - Make a new Anki note @@ -155,11 +139,11 @@ - Audio + TTS Voice - TTS Voice + Audio @@ -183,11 +167,11 @@ - The referenced resource doesn't exist. + The referenced audio program doesn't exist. - The referenced audio program doesn't exist. + The referenced resource doesn't exist. @@ -851,6 +835,10 @@ between classic and school orthography in cyrillic) Add folder + + Clear All + + Favorites: @@ -859,6 +847,14 @@ between classic and school orthography in cyrillic) All selected items will be deleted. Continue? + + Clear All Items + + + + Are you sure you want to clear all items? + + Forvo::ForvoArticleRequest @@ -886,10 +882,6 @@ between classic and school orthography in cyrillic) from - - Go to Edit|Dictionaries|Sources|Forvo and apply for our own API key to make this error disappear. - - FtsSearchPanel @@ -2163,10 +2155,6 @@ between classic and school orthography in cyrillic) MainWindow - - Welcome! - - &File @@ -2404,59 +2392,59 @@ between classic and school orthography in cyrillic) - Export to list + Show Names in Dictionary &Bar - Show Names in Dictionary &Bar + Show &Small Icons in Toolbars - &Menubar + Show &Large Icons in Toolbars - &Navigation + Show &Normal Icons in Toolbars - Back + &Menubar - Forward + &Navigation - Pronounce Word (Alt+S) + Back - Zoom In + Forward - Zoom Out + Toggle clipboard monitoring - Normal Size + Pronounce Word (Alt+S) - Found in Dictionaries: + Zoom In - Words Zoom In + Zoom Out - Words Zoom Out + Normal Size - Words Normal Size + Found in Dictionaries: @@ -2487,6 +2475,10 @@ between classic and school orthography in cyrillic) New Tab + + Welcome! + + Accessibility API is not enabled @@ -2561,11 +2553,19 @@ To find '*', '?', '[', ']' symbols use & - Article, Complete (*.html) + Complete Html (*.html *.htm) + + + + Single Html (*.html *.htm) - Article, HTML Only (*.html) + Pdf (*.pdf) + + + + Mime Html (*.mhtml) @@ -2633,23 +2633,19 @@ To find '*', '?', '[', ']' symbols use & - XML files (*.xml);;All files (*.*) + Text files (*.txt);;XML files (*.xml) Favorites export complete - - Export Favorites to file as plain list - - Import Favorites from file - XML files (*.xml);;Txt files (*.txt);;All files (*.*) + Text and XML files (*.txt *.xml);;All files (*.*) @@ -2680,22 +2676,6 @@ To find '*', '?', '[', ']' symbols use & Remove headword "%1" from Favorites? - - Show &Small Icons in Toolbars - - - - Show &Large Icons in Toolbars - - - - Show &Normal Icons in Toolbars - - - - Toggle clipboard monitoring - - Mdx::MdxArticleRequest @@ -3027,6 +3007,14 @@ the application. &Popup + + Chooses whether the clipboard monitoring will be turned on by default at the program's startup. + + + + Start with clipboard monitoring turned on + + Send translated word to main window instead of to show it in popup window @@ -3140,6 +3128,10 @@ in the pressed state when the word selection changes. Auto-pronounce words in main window + + Auto-pronounce words in the popup + + Playback @@ -3502,31 +3494,35 @@ from Stardict, Babylon and GLS dictionaries - Restart the program to apply the language change. + On - Restart to apply the interface style change. + Off - Restart to apply the interface font change. + Auto - Restart needed + Auto does nothing on some systems. - Chooses whether the clipboard monitoring will be turned on by default at the program's startup. + Restart the program to apply the language change. - Start with clipboard monitoring turned on + Restart to apply the interface style change. - Auto-pronounce words in the popup + Restart to apply the interface font change. + + + + Restart needed @@ -3887,11 +3883,6 @@ of the appropriate groups to use them. Any websites. A string %GDWORD% will be replaced with the query word: - - Alternatively, use %GD1251% for CP1251, %GDISO1%...%GDISO16% for ISO 8859-1...ISO 8859-16 respectively, -%GDBIG5% for Big-5, %GDBIG5HKSCS% for Big5-HKSCS, %GDGBK% for GBK and GB18030, %GDSHIFTJIS% for Shift-JIS. - - DICT servers diff --git a/src/common/iconv.cc b/src/common/iconv.cc index adb7c60ce..30cb7afea 100644 --- a/src/common/iconv.cc +++ b/src/common/iconv.cc @@ -19,6 +19,50 @@ Iconv::~Iconv() iconv_close( state ); } +QByteArray Iconv::fromUnicode( const QString & input, const char * toEncoding ) +{ + // Convert QString to UTF-8 + QByteArray utf8Data = input.toUtf8(); + const char * inBuf = utf8Data.constData(); + size_t inBytesLeft = utf8Data.size(); + + // Initialize iconv + iconv_t cd = iconv_open( toEncoding, "UTF-8" ); + if ( cd == (iconv_t)-1 ) { + throw std::runtime_error( "iconv_open failed" ); + } + + // Prepare output buffer + size_t outBytesLeft = inBytesLeft * 4; // Allocate enough space + std::vector< char > outBuf( outBytesLeft ); + char * outBufPtr = outBuf.data(); + + // Perform conversion + while ( inBytesLeft > 0 ) { + size_t result = iconv( cd, const_cast< char ** >( &inBuf ), &inBytesLeft, &outBufPtr, &outBytesLeft ); + if ( result == (size_t)-1 ) { + if ( errno == E2BIG ) { + // Grow the buffer and retry + size_t offset = outBufPtr - outBuf.data(); + outBuf.resize( outBuf.size() + inBytesLeft * 4 ); + outBufPtr = outBuf.data() + offset; + outBytesLeft += inBytesLeft * 4; + } + else { + iconv_close( cd ); + throw std::runtime_error( "iconv conversion failed" ); + } + } + } + + // Clean up + iconv_close( cd ); + + // Resize output buffer to actual size + outBuf.resize( outBuf.size() - outBytesLeft ); + return QByteArray( outBuf.data(), outBuf.size() ); +} + QString Iconv::convert( void const *& inBuf, size_t & inBytesLeft ) { size_t dsz = inBytesLeft; @@ -121,3 +165,14 @@ QString Iconv::toQString( char const * fromEncoding, void const * fromData, size Iconv ic( fromEncoding ); return ic.convert( fromData, dataSize ); } +QString Iconv::findValidEncoding( const QStringList & encodings ) +{ + for ( const QString & encoding : encodings ) { + iconv_t cd = iconv_open( "UTF-8", encoding.toUtf8().constData() ); + if ( cd != (iconv_t)-1 ) { + iconv_close( cd ); + return encoding; + } + } + return {}; +} diff --git a/src/common/iconv.hh b/src/common/iconv.hh index 7c6a1b639..be3eab533 100644 --- a/src/common/iconv.hh +++ b/src/common/iconv.hh @@ -6,6 +6,7 @@ #include "ex.hh" #include "text.hh" #include +#include #include /// "Internationalization conversion" for char encoding conversion, currently implemented with iconv() @@ -22,6 +23,7 @@ public: explicit Iconv( char const * from ); ~Iconv(); + static QByteArray fromUnicode( const QString & input, const char * toEncoding ); QString convert( void const *& inBuf, size_t & inBytesLeft ); @@ -34,7 +36,8 @@ public: static std::string toUtf8( char const * fromEncoding, std::u32string_view str ); static QString toQString( char const * fromEncoding, void const * fromData, size_t dataSize ); - + // tries to find a valid encoding from the given list of encodings. + static QString findValidEncoding( const QStringList & encodings ); // Copying/assigning isn't supported Q_DISABLE_COPY_MOVE( Iconv ); }; diff --git a/src/common/utils.cc b/src/common/utils.cc index b431e2fd9..6131cb473 100644 --- a/src/common/utils.cc +++ b/src/common/utils.cc @@ -5,7 +5,6 @@ #include #include #include -#include using std::string; namespace Utils { @@ -126,46 +125,6 @@ QString urlReplaceWord( const QString url, QString inputWord ) urlString.replace( "%25GDWORD%25", inputWord.toUtf8().toPercentEncoding() ); - QTextCodec * codec = QTextCodec::codecForName( "Windows-1251" ); - if ( codec ) { - urlString.replace( "%25GD1251%25", codec->fromUnicode( inputWord ).toPercentEncoding() ); - } - - codec = QTextCodec::codecForName( "Big-5" ); - if ( codec ) { - urlString.replace( "%25GDBIG5%25", codec->fromUnicode( inputWord ).toPercentEncoding() ); - } - - codec = QTextCodec::codecForName( "Big5-HKSCS" ); - if ( codec ) { - urlString.replace( "%25GDBIG5HKSCS%25", codec->fromUnicode( inputWord ).toPercentEncoding() ); - } - - codec = QTextCodec::codecForName( "Shift-JIS" ); - if ( codec ) { - urlString.replace( "%25GDSHIFTJIS%25", codec->fromUnicode( inputWord ).toPercentEncoding() ); - } - - codec = QTextCodec::codecForName( "GB18030" ); - if ( codec ) { - urlString.replace( "%25GDGBK%25", codec->fromUnicode( inputWord ).toPercentEncoding() ); - } - - - // Handle all ISO-8859 encodings - for ( int x = 1; x <= 16; ++x ) { - codec = QTextCodec::codecForName( QString( "ISO 8859-%1" ).arg( x ).toLatin1() ); - if ( codec ) { - urlString.replace( QString( "%25GDISO%1%25" ).arg( x ).toUtf8(), - codec->fromUnicode( inputWord ).toPercentEncoding() ); - } - - // Skip encodings 11..12, they don't exist - if ( x == 10 ) { - x = 12; - } - } - return urlString; } } // namespace Utils::WebSite diff --git a/src/common/utils.hh b/src/common/utils.hh index 69c7e133c..c46771429 100644 --- a/src/common/utils.hh +++ b/src/common/utils.hh @@ -273,7 +273,7 @@ inline bool isAudioUrl( QUrl const & url ) return false; // gdau links are known to be audios, (sometimes they may not have file extension). - if ( url.scheme() == "gdau" ) { + if ( url.scheme() == "gdau" || url.scheme() == "gdprg" || url.scheme() == "gdtts" ) { return true; } diff --git a/src/config.cc b/src/config.cc index f2db7833b..e1218f920 100644 --- a/src/config.cc +++ b/src/config.cc @@ -966,9 +966,7 @@ Class load() c.preferences.proxyServer.host = proxy.namedItem( "host" ).toElement().text(); c.preferences.proxyServer.port = proxy.namedItem( "port" ).toElement().text().toULong(); c.preferences.proxyServer.user = proxy.namedItem( "user" ).toElement().text(); - c.preferences.proxyServer.password = proxy.namedItem( "password" ).toElement().text(); - c.preferences.proxyServer.systemProxyUser = proxy.namedItem( "systemProxyUser" ).toElement().text(); - c.preferences.proxyServer.systemProxyPassword = proxy.namedItem( "systemProxyPassword" ).toElement().text(); + c.preferences.proxyServer.password = proxy.namedItem( "password" ).toElement().text(); } QDomNode ankiConnectServer = preferences.namedItem( "ankiConnectServer" ); @@ -1971,14 +1969,6 @@ void save( Class const & c ) opt = dd.createElement( "password" ); opt.appendChild( dd.createTextNode( c.preferences.proxyServer.password ) ); proxy.appendChild( opt ); - - opt = dd.createElement( "systemProxyUser" ); - opt.appendChild( dd.createTextNode( c.preferences.proxyServer.systemProxyUser ) ); - proxy.appendChild( opt ); - - opt = dd.createElement( "systemProxyPassword" ); - opt.appendChild( dd.createTextNode( c.preferences.proxyServer.systemProxyPassword ) ); - proxy.appendChild( opt ); } //anki connect diff --git a/src/config.hh b/src/config.hh index 974f1dcc1..cd205f83f 100644 --- a/src/config.hh +++ b/src/config.hh @@ -164,7 +164,6 @@ struct ProxyServer QString host; unsigned port; QString user, password; - QString systemProxyUser, systemProxyPassword; ProxyServer(); }; diff --git a/src/dict/btreeidx.hh b/src/dict/btreeidx.hh index b8513e011..80bcb1cd5 100644 --- a/src/dict/btreeidx.hh +++ b/src/dict/btreeidx.hh @@ -25,7 +25,9 @@ enum { /// This is to be bumped up each time the internal format changes. /// The value isn't used here by itself, it is supposed to be added /// to each dictionary's internal format version. - FormatVersion = 4 + FormatVersion = 4, + //the indexedzip parse logic version + ZipParseLogicVersion = 1 }; // These exceptions which might be thrown during the index traversal diff --git a/src/dict/dictionary.cc b/src/dict/dictionary.cc index 51772923d..de6cd9b89 100644 --- a/src/dict/dictionary.cc +++ b/src/dict/dictionary.cc @@ -606,16 +606,13 @@ string getFtsSuffix() QString generateRandomDictionaryId() { - return QString( - QCryptographicHash::hash( QDateTime::currentDateTime().toString( "\"Random\"dd.MM.yyyy hh:mm:ss.zzz" ).toUtf8(), - QCryptographicHash::Md5 ) - .toHex() ); + return QCryptographicHash::hash( QUuid::createUuid().toString().toUtf8(), QCryptographicHash::Md5 ).toHex(); } QMap< std::string, sptr< Dictionary::Class > > dictToMap( std::vector< sptr< Dictionary::Class > > const & dicts ) { QMap< std::string, sptr< Dictionary::Class > > dictMap; - for ( auto & dict : dicts ) { + for ( const auto & dict : dicts ) { if ( !dict ) { continue; } diff --git a/src/dict/dsl.cc b/src/dict/dsl.cc index ae6117171..ffd006aec 100644 --- a/src/dict/dsl.cc +++ b/src/dict/dsl.cc @@ -58,7 +58,7 @@ DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex ) enum { Signature = 0x584c5344, // DSLX on little-endian, XLSD on big-endian - CurrentFormatVersion = 23 + BtreeIndexing::FormatVersion + Folding::Version, + CurrentFormatVersion = 23 + BtreeIndexing::FormatVersion + Folding::Version + BtreeIndexing::ZipParseLogicVersion, CurrentZipSupportVersion = 2, CurrentFtsIndexVersion = 7 }; diff --git a/src/dict/dsl_details.cc b/src/dict/dsl_details.cc index 96a43e478..2387ad4aa 100644 --- a/src/dict/dsl_details.cc +++ b/src/dict/dsl_details.cc @@ -894,9 +894,7 @@ DslScanner::DslScanner( string const & fileName ): } } - codec = QTextCodec::codecForName( getEncodingNameFor( encoding ) ); - - qDebug() << "DSL encoding ->" << codec->name(); + qDebug() << "DSL encoding:" << getEncodingNameFor( encoding ); if ( gzrewind( f ) ) { gzclose( f ); @@ -1039,7 +1037,7 @@ bool DslScanner::readNextLine( std::u32string & out, size_t & offset, bool only_ if ( pos == -1 ) { return false; } - QString line = codec->toUnicode( readBufferPtr, pos ); + QString line = Iconv::toQString( getEncodingNameFor( encoding ), readBufferPtr, pos ); line = Utils::rstrip( line ); if ( pos > readBufferLeft ) { diff --git a/src/dict/dsl_details.hh b/src/dict/dsl_details.hh index 61a5b6217..e10267cd2 100644 --- a/src/dict/dsl_details.hh +++ b/src/dict/dsl_details.hh @@ -9,7 +9,6 @@ #include #include "dictionary.hh" #include "iconv.hh" -#include #include #include "text.hh" @@ -102,7 +101,6 @@ class DslScanner { gzFile f; Encoding encoding; - QTextCodec * codec; std::u32string dictionaryName; std::u32string langFrom, langTo; std::u32string soundDictionary; diff --git a/src/dict/epwing_book.cc b/src/dict/epwing_book.cc index 87a10ffcd..70d9ff442 100644 --- a/src/dict/epwing_book.cc +++ b/src/dict/epwing_book.cc @@ -14,6 +14,7 @@ #include "folding.hh" #include "epwing_charmap.hh" #include "htmlescape.hh" + #include "iconv.hh" #if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC ) #define _FILE_OFFSET_BITS 64 #endif @@ -146,10 +147,8 @@ EB_Error_Code hook_iso8859_1( EB_Book * book, EB_Appendix *, void * container, EB_Hook_Code, int, const unsigned int * argv ) { EpwingBook * ebook = static_cast< EpwingBook * >( container ); - if ( ebook->codecISO() ) { - QByteArray b = ebook->codecISO()->toUnicode( (const char *)argv, 1 ).toUtf8(); - eb_write_text( book, b.data(), b.size() ); - } + QByteArray b = Iconv::toQString( ebook->codec_ISO_name, (const char *)argv, 1 ).toUtf8(); + eb_write_text( book, b.data(), b.size() ); return EB_SUCCESS; } @@ -175,15 +174,12 @@ hook_narrow_jisx0208( EB_Book * book, EB_Appendix *, void * container, EB_Hook_C if ( out_code == 0 ) { EContainer * cont = static_cast< EContainer * >( container ); - if ( cont->book->codecEuc() ) { - QByteArray str = cont->book->codecEuc()->toUnicode( (const char *)buf, 2 ).toUtf8(); - eb_write_text( book, str.data(), str.size() ); - } - else - eb_write_text( book, (const char *)buf, 2 ); + QByteArray str = Iconv::toQString( cont->book->codec_Euc_name, (const char *)buf, 2 ).toUtf8(); + eb_write_text( book, str.data(), str.size() ); } - else + else { eb_write_text_byte1( book, out_code ); + } } return EB_SUCCESS; @@ -198,12 +194,8 @@ hook_wide_jisx0208( EB_Book * book, EB_Appendix *, void * ptr, EB_Hook_Code, int buf[ 1 ] = *argv & 0xFF; buf[ 0 ] = ( *argv & 0xFF00 ) >> 8; - if ( ebook->codecEuc() ) { - QByteArray b = ebook->codecEuc()->toUnicode( buf, 2 ).toUtf8(); - eb_write_text( book, b.data(), b.size() ); - } - else - eb_write_text_byte2( book, buf[ 0 ], buf[ 1 ] ); + QByteArray b = Iconv::toQString( ebook->codec_Euc_name, buf, 2 ).toUtf8(); + eb_write_text( book, b.data(), b.size() ); return EB_SUCCESS; } @@ -217,12 +209,8 @@ hook_gb2312( EB_Book * book, EB_Appendix *, void * container, EB_Hook_Code, int, buf[ 1 ] = *argv & 0xFF; buf[ 0 ] = ( *argv & 0xFF00 ) >> 8; - if ( ebook->codecGB() ) { - QByteArray b = ebook->codecGB()->toUnicode( buf, 2 ).toUtf8(); - eb_write_text( book, b.data(), b.size() ); - } - else - eb_write_text_byte2( book, buf[ 0 ], buf[ 1 ] ); + QByteArray b = Iconv::toQString( ebook->codec_GB_name, buf, 2 ).toUtf8(); + eb_write_text( book, b.data(), b.size() ); return EB_SUCCESS; } @@ -397,9 +385,9 @@ hook_candidate( EB_Book * book, EB_Appendix *, void * container, EB_Hook_Code co EpwingBook::EpwingBook(): currentSubBook( -1 ) { - codec_ISO = QTextCodec::codecForName( "ISO8859-1" ); - codec_GB = QTextCodec::codecForName( "GB2312" ); - codec_Euc = QTextCodec::codecForName( "EUC-JP" ); + codec_ISO_name = "ISO8859-1"; + codec_GB_name = "GB2312"; + codec_Euc_name = "EUC-JP"; eb_initialize_book( &book ); eb_initialize_appendix( &appendix ); @@ -422,8 +410,8 @@ void EpwingBook::setErrorString( QString const & func, EB_Error_Code code ) { error_string = QString( "EB \"%1\" function error: %2 (%3)" ) .arg( func ) - .arg( QTextCodec::codecForLocale()->toUnicode( eb_error_string( code ) ) ) - .arg( QTextCodec::codecForLocale()->toUnicode( eb_error_message( code ) ) ); + .arg( QString::fromLocal8Bit( eb_error_string( code ) ) ) + .arg( QString::fromLocal8Bit( eb_error_message( code ) ) ); if ( currentPosition.page != 0 ) error_string += QString( " on page %1, offset %2" ) @@ -488,9 +476,6 @@ int EpwingBook::setBook( string const & directory ) setErrorString( "eb_appendix_subbook_list", ret ); } - if ( !codec_Euc || ( book.character_code == EB_CHARCODE_ISO8859_1 && !codec_ISO ) - || ( book.character_code == EB_CHARCODE_JISX0208_GB2312 && !codec_GB ) ) - throw exEpwing( "No required codec to decode dictionary" ); rootDir = QString::fromStdString( directory ); @@ -657,10 +642,7 @@ QString EpwingBook::title() } buf[ EB_MAX_TITLE_LENGTH ] = 0; - if ( codec_Euc ) - return codec_Euc->toUnicode( buf ); - - return {}; + return Iconv::toQString( codec_Euc_name, buf, strlen( buf ) ); } QString EpwingBook::copyright() @@ -1086,14 +1068,12 @@ bool EpwingBook::isHeadwordCorrect( QString const & headword ) if ( headword.isEmpty() ) return false; - if ( book.character_code == EB_CHARCODE_ISO8859_1 && codec_ISO ) - buf = codec_ISO->fromUnicode( headword ); - else if ( ( book.character_code == EB_CHARCODE_JISX0208 || book.character_code == EB_CHARCODE_JISX0208_GB2312 ) - && codec_Euc ) - buf = codec_Euc->fromUnicode( headword ); - - if ( book.character_code == EB_CHARCODE_JISX0208_GB2312 && codec_GB ) - buf2 = codec_GB->fromUnicode( headword ); + if ( book.character_code == EB_CHARCODE_ISO8859_1 ) + buf = Iconv::fromUnicode( headword, codec_ISO_name ); + else if ( ( book.character_code == EB_CHARCODE_JISX0208 || book.character_code == EB_CHARCODE_JISX0208_GB2312 ) ) + buf = Iconv::fromUnicode( headword, codec_Euc_name ); + if ( book.character_code == EB_CHARCODE_JISX0208_GB2312 ) + buf2 = Iconv::fromUnicode( headword, codec_GB_name ); if ( !buf.isEmpty() && eb_search_exactword( &book, buf.data() ) == EB_SUCCESS ) { ret = eb_hit_list( &book, 2, hits, &hit_count ); @@ -1846,9 +1826,7 @@ QString EpwingBook::currentCandidate() const char * s = eb_current_candidate( &book ); if ( book.character_code == EB_CHARCODE_ISO8859_1 ) return QString::fromLatin1( s ); - if ( codec_Euc ) - return codec_Euc->toUnicode( s ); - return QString{}; + return Iconv::toQString( codec_Euc_name, s, strlen( s ) ); } bool EpwingBook::getMatches( QString word, QList< QString > & matches ) @@ -1857,14 +1835,13 @@ bool EpwingBook::getMatches( QString word, QList< QString > & matches ) EB_Hit hits[ HitsBufferSize ]; int hitCount = 0; - if ( book.character_code == EB_CHARCODE_ISO8859_1 && codec_ISO ) - bword = codec_ISO->fromUnicode( word ); - else if ( ( book.character_code == EB_CHARCODE_JISX0208 || book.character_code == EB_CHARCODE_JISX0208_GB2312 ) - && codec_Euc ) - bword = codec_Euc->fromUnicode( word ); + if ( book.character_code == EB_CHARCODE_ISO8859_1 ) + bword = Iconv::fromUnicode( word, codec_ISO_name ); + else if ( ( book.character_code == EB_CHARCODE_JISX0208 || book.character_code == EB_CHARCODE_JISX0208_GB2312 ) ) + bword = Iconv::fromUnicode( word, codec_Euc_name ); - if ( book.character_code == EB_CHARCODE_JISX0208_GB2312 && codec_GB ) - bword2 = codec_GB->fromUnicode( word ); + if ( book.character_code == EB_CHARCODE_JISX0208_GB2312 ) + bword2 = Iconv::fromUnicode( word, codec_GB_name ); if ( !bword.isEmpty() ) { EB_Error_Code ret = eb_search_word( &book, bword.data() ); @@ -1928,14 +1905,13 @@ bool EpwingBook::getArticlePos( QString word, QList< int > & pages, QList< int > EB_Hit hits[ HitsBufferSize ]; int hitCount = 0; - if ( book.character_code == EB_CHARCODE_ISO8859_1 && codec_ISO ) - bword = codec_ISO->fromUnicode( word ); - else if ( ( book.character_code == EB_CHARCODE_JISX0208 || book.character_code == EB_CHARCODE_JISX0208_GB2312 ) - && codec_Euc ) - bword = codec_Euc->fromUnicode( word ); + if ( book.character_code == EB_CHARCODE_ISO8859_1 ) + bword = Iconv::fromUnicode( word, codec_ISO_name ); + else if ( ( book.character_code == EB_CHARCODE_JISX0208 || book.character_code == EB_CHARCODE_JISX0208_GB2312 ) ) + bword = Iconv::fromUnicode( word, codec_Euc_name ); - if ( book.character_code == EB_CHARCODE_JISX0208_GB2312 && codec_GB ) - bword2 = codec_GB->fromUnicode( word ); + if ( book.character_code == EB_CHARCODE_JISX0208_GB2312 ) + bword2 = Iconv::fromUnicode( word, codec_GB_name ); if ( !bword.isEmpty() ) { EB_Error_Code ret = eb_search_exactword( &book, bword.data() ); diff --git a/src/dict/epwing_book.hh b/src/dict/epwing_book.hh index 9a02413d4..c5606225f 100644 --- a/src/dict/epwing_book.hh +++ b/src/dict/epwing_book.hh @@ -16,7 +16,6 @@ #endif #include -#include // POSIX symbol unavailable on Windows needed for eb headers #ifdef Q_OS_WIN @@ -72,7 +71,6 @@ class EpwingBook QString mainCacheDir, rootDir; QString cacheImagesDir, cacheSoundsDir, cacheMoviesDir, cacheFontsDir; QString dictID; - QTextCodec *codec_ISO, *codec_GB, *codec_Euc; QStack< unsigned int > decorationStack; int monoWidth, monoHeight; QStringList imageCacheList, soundsCacheList, moviesCacheList, fontsCacheList; @@ -110,6 +108,7 @@ class EpwingBook QByteArray codeToUnicode( QString const & code ); public: + const char *codec_ISO_name, *codec_GB_name, *codec_Euc_name; enum DecorationCodes { UNKNOWN = 0, @@ -133,21 +132,6 @@ public: return error_string; } - QTextCodec * codecISO() - { - return codec_ISO; - } - - QTextCodec * codecGB() - { - return codec_GB; - } - - QTextCodec * codecEuc() - { - return codec_Euc; - } - int getSubBookCount() { return subBookCount; diff --git a/src/dict/forvo.cc b/src/dict/forvo.cc index 6caa752e8..2d62cb874 100644 --- a/src/dict/forvo.cc +++ b/src/dict/forvo.cc @@ -313,23 +313,28 @@ void ForvoArticleRequest::requestFinished( QNetworkReply * r ) if ( !errors.isNull() ) { QString text( errors.namedItem( "error" ).toElement().text() ); - - if ( text == "Limit/day reached." && apiKey.simplified().isEmpty() ) { - // Give a hint that the user should apply for his own key. - - text += - "\n" - + tr( - "Go to Edit|Dictionaries|Sources|Forvo and apply for our own API key to make this error disappear." ); - } - setErrorString( text ); } } qDebug( "done." ); } else { - setErrorString( netReply->errorString() ); + //forvo return the error message with http status code=400. + QDomDocument dd; + + QString errorStr; + int errorLine, errorColumn; + + if ( !dd.setContent( netReply.get(), false, &errorStr, &errorLine, &errorColumn ) ) { + setErrorString( netReply->errorString() ); + } + else { + QDomNode errors = dd.namedItem( "errors" ); + if ( !errors.isNull() ) { + QString text( errors.namedItem( "error" ).toElement().text() ); + setErrorString( text ); + } + } } } diff --git a/src/dict/gls.cc b/src/dict/gls.cc index c1ad48617..2b7e52c05 100644 --- a/src/dict/gls.cc +++ b/src/dict/gls.cc @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -51,7 +50,6 @@ class GlsScanner { gzFile f; Encoding encoding; - QTextCodec * codec; std::u32string dictionaryName; std::u32string dictionaryDecription, dictionaryAuthor; std::u32string langFrom, langTo; @@ -171,7 +169,6 @@ GlsScanner::GlsScanner( string const & fileName ): encoding = Encoding::Utf8; } - codec = QTextCodec::codecForName( Text::getEncodingNameFor( encoding ) ); // We now can use our own readNextLine() function lineFeed = Text::initLineFeed( encoding ); @@ -257,7 +254,8 @@ bool GlsScanner::readNextLine( std::u32string & out, size_t & offset ) if ( pos == -1 ) { return false; } - QString line = codec->toUnicode( readBufferPtr, pos ); + + QString line = Iconv::toQString( Text::getEncodingNameFor( encoding ), readBufferPtr, pos ); line = Utils::rstrip( line ); @@ -287,7 +285,7 @@ DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex ) enum { Signature = 0x58534c47, // GLSX on little-endian, XSLG on big-endian - CurrentFormatVersion = 1 + BtreeIndexing::FormatVersion + Folding::Version, + CurrentFormatVersion = 1 + BtreeIndexing::FormatVersion + Folding::Version + BtreeIndexing::ZipParseLogicVersion, CurrentZipSupportVersion = 2, CurrentFtsIndexVersion = 1 }; diff --git a/src/dict/lingualibre.cc b/src/dict/lingualibre.cc index e77b7fd1b..3d39b516c 100644 --- a/src/dict/lingualibre.cc +++ b/src/dict/lingualibre.cc @@ -342,23 +342,32 @@ void LinguaArticleRequest::requestFinished( QNetworkReply * r ) if ( resultJson.contains( "query" ) ) { - string articleBody = "

"; + string articleBody = "

"; for ( auto pageJsonVal : resultJson[ "query" ].toObject()[ "pages" ].toObject() ) { auto pageJsonObj = pageJsonVal.toObject(); string title = pageJsonObj[ "title" ].toString().toHtmlEscaped().toStdString(); string audiolink = pageJsonObj[ "imageinfo" ].toArray().at( 0 ).toObject()[ "url" ].toString().toHtmlEscaped().toStdString(); - articleBody += addAudioLink( audiolink, dictionaryId ); + addAudioLink( audiolink, dictionaryId ); + + articleBody += "
"; + //play icon articleBody += R"()"; + articleBody += R"(" role="button">)"; articleBody += R"(Play)"; + articleBody += ""; + //text + articleBody += R"()"; articleBody += title; - articleBody += "
"; + articleBody += ""; + articleBody += "
"; } - articleBody += "

"; + articleBody += "
"; appendString( articleBody ); diff --git a/src/dict/lsa.cc b/src/dict/lsa.cc index 00c858c08..fb9f2b39c 100644 --- a/src/dict/lsa.cc +++ b/src/dict/lsa.cc @@ -258,9 +258,9 @@ sptr< Dictionary::DataRequest > LsaDictionary::getArticle( std::u32string const multimap< std::u32string, string >::const_iterator i; - result += ""; + result += "
"; for ( i = mainArticles.begin(); i != mainArticles.end(); ++i ) { - result += "
"; + result += "
"; QUrl url; url.setScheme( "gdau" ); @@ -269,15 +269,15 @@ sptr< Dictionary::DataRequest > LsaDictionary::getArticle( std::u32string const string ref = string( "\"" ) + url.toEncoded().data() + "\""; - result += addAudioLink( url.toEncoded(), getId() ); + addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += ""; + result += "Play)"; + result += "" + i->second + ""; + result += ""; } for ( i = alternateArticles.begin(); i != alternateArticles.end(); ++i ) { - result += ""; + result += "
"; QUrl url; url.setScheme( "gdau" ); @@ -288,12 +288,12 @@ sptr< Dictionary::DataRequest > LsaDictionary::getArticle( std::u32string const result += addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += ""; + result += "Play)"; + result += "" + i->second + ""; + result += ""; } - result += "
Play" + i->second + "
Play" + i->second + "
"; + result += ""; auto * ret = new Dictionary::DataRequestInstant( true ); diff --git a/src/dict/mdictparser.cc b/src/dict/mdictparser.cc index 83b48c0ac..ce6b76fd5 100644 --- a/src/dict/mdictparser.cc +++ b/src/dict/mdictparser.cc @@ -32,12 +32,11 @@ #include #include #include -#include - #include "decompress.hh" #include "ripemd.hh" #include "utils.hh" #include "htmlescape.hh" +#include "iconv.hh" namespace Mdict { @@ -76,21 +75,9 @@ size_t MdictParser::RecordIndex::bsearch( const vector< MdictParser::RecordIndex return (size_t)( -1 ); } - size_t lo = 0; - size_t hi = offsets.size() - 1; - - while ( lo <= hi ) { - size_t mid = ( lo + hi ) >> 1; - RecordIndex const & p = offsets[ mid ]; - if ( p == val ) { - return mid; - } - else if ( p < val ) { - lo = mid + 1; - } - else { - hi = mid - 1; - } + auto it = std::lower_bound( offsets.begin(), offsets.end(), val ); + if ( it != offsets.end() && *it == val ) { + return std::distance( offsets.begin(), it ); } return (size_t)( -1 ); @@ -187,8 +174,7 @@ QString MdictParser::toUtf16( const char * fromCode, const char * from, size_t f return QString(); } - QTextCodec * codec = QTextCodec::codecForName( fromCode ); - return codec->toUnicode( from, fromSize ); + return Iconv::toQString( fromCode, from, fromSize ); } bool MdictParser::decryptHeadWordIndex( char * buffer, qint64 len ) diff --git a/src/dict/programs.cc b/src/dict/programs.cc index 9052d4921..65ba22d0e 100644 --- a/src/dict/programs.cc +++ b/src/dict/programs.cc @@ -83,7 +83,7 @@ sptr< Dictionary::DataRequest > ProgramsDictionary::getArticle( std::u32string c string wordUtf8( Text::toUtf8( word ) ); - result += ""; + result += "
"; QUrl url; url.setScheme( "gdprg" ); @@ -92,11 +92,11 @@ sptr< Dictionary::DataRequest > ProgramsDictionary::getArticle( std::u32string c string ref = string( "\"" ) + url.toEncoded().data() + "\""; - result += addAudioLink( url.toEncoded(), getId() ); + addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += "
Play" + Html::escape( wordUtf8 ) + "
"; + result += "Play)"; + result += "" + Html::escape( wordUtf8 ) + ""; + result += ""; auto ret = std::make_shared< DataRequestInstant >( true ); ret->appendString( result ); diff --git a/src/dict/sounddir.cc b/src/dict/sounddir.cc index 23a707f28..6f66320ed 100644 --- a/src/dict/sounddir.cc +++ b/src/dict/sounddir.cc @@ -176,7 +176,7 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getArticle( std::u32string c vector< char > chunk; char * nameBlock; - result += ""; + result += "
"; for ( i = mainArticles.begin(); i != mainArticles.end(); ++i ) { uint32_t address = chain[ i->second ].articleOffset; @@ -204,7 +204,7 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getArticle( std::u32string c } } - result += "
"; + result += "
"; auto _displayName = Html::escape( displayedName ); QString file_name; if ( !get_file_name( address, file_name ) ) { @@ -221,9 +221,9 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getArticle( std::u32string c result += addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += ""; + result += "Play)"; + result += "" + _displayName + ""; + result += ""; } for ( i = alternateArticles.begin(); i != alternateArticles.end(); ++i ) { @@ -252,7 +252,7 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getArticle( std::u32string c } } - result += ""; + result += "
"; auto _displayName = Html::escape( displayedName ); QString file_name; if ( !get_file_name( address, file_name ) ) { @@ -267,14 +267,14 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getArticle( std::u32string c string ref = string( "\"" ) + url.toEncoded().data() + "\""; - result += addAudioLink( url.toEncoded(), getId() ); + addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += ""; + result += "Play)"; + result += "" + _displayName + ""; + result += ""; } - result += "
Play" + _displayName + "
Play" + _displayName + "
"; + result += ""; auto ret = std::make_shared< Dictionary::DataRequestInstant >( true ); diff --git a/src/dict/stardict.cc b/src/dict/stardict.cc index 941f55ca2..778b60ede 100644 --- a/src/dict/stardict.cc +++ b/src/dict/stardict.cc @@ -78,7 +78,7 @@ struct Ifo enum { Signature = 0x58444953, // SIDX on little-endian, XDIS on big-endian - CurrentFormatVersion = 9 + BtreeIndexing::FormatVersion + Folding::Version + CurrentFormatVersion = 9 + BtreeIndexing::FormatVersion + Folding::Version + BtreeIndexing::ZipParseLogicVersion }; #pragma pack( push, 1 ) diff --git a/src/dict/utils/indexedzip.cc b/src/dict/utils/indexedzip.cc index f0e2caa57..55d536755 100644 --- a/src/dict/utils/indexedzip.cc +++ b/src/dict/utils/indexedzip.cc @@ -6,8 +6,6 @@ #include #include "text.hh" #include "iconv.hh" -#include - #include using namespace BtreeIndexing; @@ -60,12 +58,13 @@ bool IndexedZip::loadFile( uint32_t offset, vector< char > & data ) return false; } + //the offset is central dir header position. ZipFile::LocalFileHeader header; - if ( !ZipFile::readLocalHeader( zip, header ) ) { + if ( !ZipFile::readLocalHeaderFromCentral( zip, header ) ) { vector< string > zipFileNames; zip.getFilenames( zipFileNames ); - qDebug( "Failed to load header" ); + qDebug() << "Failed to load header"; string filename; if ( zip.getCurrentFile() < zipFileNames.size() ) { filename = zipFileNames.at( zip.getCurrentFile() ); @@ -75,26 +74,34 @@ bool IndexedZip::loadFile( uint32_t offset, vector< char > & data ) return false; } - // Which algorithm was used? + zip.seek( header.offset ); + if ( !ZipFile::skipLocalHeader( zip ) ) { + qDebug() << "Failed to skip local header"; + return false; + } + // Which algorithm was used? switch ( header.compressionMethod ) { case ZipFile::Uncompressed: - qDebug( "Uncompressed" ); + qDebug() << "Uncompressed"; data.resize( header.uncompressedSize ); return (size_t)zip.read( &data.front(), data.size() ) == data.size(); case ZipFile::Deflated: { // Decompress the data using the zlib library - - QByteArray compressedData = zip.read( header.compressedSize ); - - if ( compressedData.size() != (int)header.compressedSize ) { + // Check for unusually large compressed size,100MB + if ( header.compressedSize > 100000000 ) { // Example threshold + qDebug() << "Unusually large compressed size:" << header.compressedSize; return false; } - - if ( header.uncompressedSize == 0 ) { + if ( header.compressedSize == 0 ) { //the compress data should have some issue. - qDebug() << "uncompressed size is 0;"; + qDebug() << "compressed size is 0;"; + return false; + } + QByteArray compressedData = zip.read( header.compressedSize ); + + if ( compressedData.size() != (int)header.compressedSize ) { return false; } @@ -157,119 +164,44 @@ bool IndexedZip::indexFile( BtreeIndexing::IndexedWords & zipFileNames, quint32 } // File seems to be a valid zip file - - - QTextCodec * localeCodec = QTextCodec::codecForLocale(); - ZipFile::CentralDirEntry entry; - bool alreadyCounted; if ( filesCount ) { *filesCount = 0; } while ( ZipFile::readNextEntry( zip, entry ) ) { if ( entry.compressionMethod == ZipFile::Unsupported ) { - qWarning( "Zip warning: compression method unsupported -- skipping file \"%s\"", entry.fileName.data() ); + qWarning() << "Zip warning: compression method unsupported -- skipping file" << entry.fileName.data(); continue; } - // Check if the file name has some non-ascii letters. - - unsigned char const * ptr = (unsigned char const *)entry.fileName.constData(); - - bool hasNonAscii = false; - - for ( ;; ) { - if ( *ptr & 0x80 ) { - hasNonAscii = true; - break; - } - else if ( !*ptr++ ) { - break; - } - } - - alreadyCounted = false; - - if ( !hasNonAscii ) { - // Add entry as is - - zipFileNames.addSingleWord( Text::toUtf32( entry.fileName.data() ), entry.localHeaderOffset ); + if ( entry.fileNameInUTF8 ) { + zipFileNames.addSingleWord( Text::toUtf32( entry.fileName.data() ), entry.centralHeaderOffset ); if ( filesCount ) { *filesCount += 1; } } else { - // Try assuming different encodings. Those are UTF8, system locale and two - // Russian ones (Windows and Windows OEM). Unfortunately, zip - // files do not say which encoding they utilize. - - // Utf8 try { - std::u32string decoded = Text::toUtf32( entry.fileName.constData() ); - - zipFileNames.addSingleWord( decoded, entry.localHeaderOffset ); - if ( filesCount != 0 && !alreadyCounted ) { - *filesCount += 1; - alreadyCounted = true; - } - } - catch ( Text::exCantDecode & ) { - // Failed to decode - } - - if ( !entry.fileNameInUTF8 ) { - std::u32string nameInSystemLocale; - - // System locale - if ( localeCodec ) { - QString name = localeCodec->toUnicode( entry.fileName.constData(), entry.fileName.size() ); - nameInSystemLocale = name.toStdU32String(); - if ( !nameInSystemLocale.empty() ) { - zipFileNames.addSingleWord( nameInSystemLocale, entry.localHeaderOffset ); - - if ( filesCount != 0 && !alreadyCounted ) { - *filesCount += 1; - alreadyCounted = true; - } - } + //detect encoding. + auto encoding = Iconv::findValidEncoding( { "LOCAL", "IBM437", "CP866", "CP1251", "UTF-8" } ); + if ( encoding.isEmpty() ) { + qWarning() << "Zip warning: failed to detect encoding -- skipping file" << entry.fileName.data(); + continue; } + std::u32string nameInSystemLocale = + Iconv::toWstring( encoding.toUtf8().constData(), entry.fileName.constData(), entry.fileName.size() ); + if ( !nameInSystemLocale.empty() ) { + zipFileNames.addSingleWord( nameInSystemLocale, entry.centralHeaderOffset ); - - // CP866 - try { - std::u32string decoded = Iconv::toWstring( "CP866", entry.fileName.constData(), entry.fileName.size() ); - - if ( nameInSystemLocale != decoded ) { - zipFileNames.addSingleWord( decoded, entry.localHeaderOffset ); - - if ( filesCount != 0 && !alreadyCounted ) { - *filesCount += 1; - alreadyCounted = true; - } - } - } - catch ( Iconv::Ex & ) { - // Failed to decode - } - - // CP1251 - try { - std::u32string decoded = Iconv::toWstring( "CP1251", entry.fileName.constData(), entry.fileName.size() ); - - if ( nameInSystemLocale != decoded ) { - zipFileNames.addSingleWord( decoded, entry.localHeaderOffset ); - - if ( filesCount != 0 && !alreadyCounted ) { - *filesCount += 1; - alreadyCounted = true; - } + if ( filesCount != 0 ) { + *filesCount += 1; } } - catch ( Iconv::Ex & ) { - // Failed to decode - } + } + catch ( Iconv::Ex & ) { + // Failed to decode } } } diff --git a/src/dict/utils/zipfile.cc b/src/dict/utils/zipfile.cc index c568fd3c6..44f19770d 100644 --- a/src/dict/utils/zipfile.cc +++ b/src/dict/utils/zipfile.cc @@ -136,6 +136,8 @@ bool readNextEntry( SplitZipFile & zip, CentralDirEntry & entry ) { CentralFileHeaderRecord record; + auto centralDirOffset = zip.pos(); + if ( zip.read( (char *)&record, sizeof( record ) ) != sizeof( record ) ) { return false; } @@ -160,6 +162,7 @@ bool readNextEntry( SplitZipFile & zip, CentralDirEntry & entry ) return false; } + entry.centralHeaderOffset = zip.calcAbsoluteOffset( centralDirOffset, qFromLittleEndian( record.diskNumberStart ) ); entry.localHeaderOffset = zip.calcAbsoluteOffset( qFromLittleEndian( record.offsetOfLocalHeader ), qFromLittleEndian( record.diskNumberStart ) ); entry.compressedSize = qFromLittleEndian( record.compressedSize ); @@ -170,7 +173,7 @@ bool readNextEntry( SplitZipFile & zip, CentralDirEntry & entry ) return true; } -bool readLocalHeader( SplitZipFile & zip, LocalFileHeader & entry ) +bool skipLocalHeader( SplitZipFile & zip ) { LocalFileHeaderRecord record; @@ -182,24 +185,37 @@ bool readLocalHeader( SplitZipFile & zip, LocalFileHeader & entry ) return false; } - // Read file name - + // skip file name int fileNameLength = qFromLittleEndian( record.fileNameLength ); - entry.fileName = zip.read( fileNameLength ); + // Skip extra field + return zip.seek( zip.pos() + fileNameLength + qFromLittleEndian( record.extraFieldLength ) ); +} - if ( entry.fileName.size() != fileNameLength ) { +bool readLocalHeaderFromCentral( SplitZipFile & zip, LocalFileHeader & entry ) +{ + CentralFileHeaderRecord record; + + if ( zip.read( (char *)&record, sizeof( record ) ) != sizeof( record ) ) { return false; } - // Skip extra field + if ( record.signature != centralFileHeaderSignature ) { + return false; + } - if ( !zip.seek( zip.pos() + qFromLittleEndian( record.extraFieldLength ) ) ) { + // Read file name + int fileNameLength = qFromLittleEndian( record.fileNameLength ); + entry.fileName = zip.read( fileNameLength ); + + if ( entry.fileName.size() != fileNameLength ) { return false; } entry.compressedSize = qFromLittleEndian( record.compressedSize ); entry.uncompressedSize = qFromLittleEndian( record.uncompressedSize ); entry.compressionMethod = getCompressionMethod( record.compressionMethod ); + entry.offset = zip.calcAbsoluteOffset( qFromLittleEndian( record.offsetOfLocalHeader ), + qFromLittleEndian( record.diskNumberStart ) ); return true; } diff --git a/src/dict/utils/zipfile.hh b/src/dict/utils/zipfile.hh index ed2625a47..82deea76a 100644 --- a/src/dict/utils/zipfile.hh +++ b/src/dict/utils/zipfile.hh @@ -37,7 +37,7 @@ enum CompressionMethod { struct CentralDirEntry { QByteArray fileName; - + quint32 centralHeaderOffset; quint32 localHeaderOffset, compressedSize, uncompressedSize; CompressionMethod compressionMethod; bool fileNameInUTF8; @@ -51,6 +51,7 @@ struct LocalFileHeader quint32 compressedSize, uncompressedSize; CompressionMethod compressionMethod; + quint32 offset; }; /// Finds the central directory in the given file and positions it at its @@ -65,9 +66,8 @@ bool positionAtCentralDir( SplitZipFile & ); /// Returns true on success, false otherwise. bool readNextEntry( SplitZipFile &, CentralDirEntry & ); -/// Reads loca file header from the zip at its current offset. The file gets -/// advanced by the size of entry and starts pointing to file data. -/// Returns true on success, false otherwise. -bool readLocalHeader( SplitZipFile &, LocalFileHeader & ); +/// Skips the local header of the file at the current position. the file data follows the header. +bool skipLocalHeader( SplitZipFile & zip ); +bool readLocalHeaderFromCentral( SplitZipFile &, LocalFileHeader & ); } // namespace ZipFile diff --git a/src/dict/voiceengines.cc b/src/dict/voiceengines.cc index bba36760f..71b36bf08 100644 --- a/src/dict/voiceengines.cc +++ b/src/dict/voiceengines.cc @@ -84,7 +84,7 @@ VoiceEnginesDictionary::getArticle( u32string const & word, vector< u32string > string result; string wordUtf8( Text::toUtf8( word ) ); - result += ""; + result += "
"; QUrl url; url.setScheme( "gdtts" ); @@ -96,11 +96,11 @@ VoiceEnginesDictionary::getArticle( u32string const & word, vector< u32string > string encodedUrl = url.toEncoded().data(); string ref = string( "\"" ) + encodedUrl + "\""; - result += addAudioLink( encodedUrl, getId() ); + addAudioLink( encodedUrl, getId() ); - result += "
)"; - result += ""; - result += "
Play" + Html::escape( wordUtf8 ) + "
"; + result += "Play)"; + result += "" + Html::escape( wordUtf8 ) + ""; + result += ""; auto ret = std::make_shared< DataRequestInstant >( true ); ret->appendString( result ); diff --git a/src/dict/website.cc b/src/dict/website.cc index f4d40947c..349bf25b0 100644 --- a/src/dict/website.cc +++ b/src/dict/website.cc @@ -122,7 +122,6 @@ class WebSiteArticleRequest: public WebSiteDataRequestSlots private: void requestFinished( QNetworkReply * ) override; - static QTextCodec * codecForHtml( QByteArray const & ba ); }; void WebSiteArticleRequest::cancel() @@ -152,11 +151,6 @@ WebSiteArticleRequest::WebSiteArticleRequest( QString const & url_, QNetworkAcce #endif } -QTextCodec * WebSiteArticleRequest::codecForHtml( QByteArray const & ba ) -{ - return QTextCodec::codecForHtml( ba, 0 ); -} - void WebSiteArticleRequest::requestFinished( QNetworkReply * r ) { if ( isFinished() ) { // Was cancelled @@ -188,7 +182,7 @@ void WebSiteArticleRequest::requestFinished( QNetworkReply * r ) QByteArray replyData = netReply->readAll(); QString articleString; - QTextCodec * codec = WebSiteArticleRequest::codecForHtml( replyData ); + QTextCodec * codec = QTextCodec::codecForHtml( replyData, 0 ); if ( codec ) { articleString = codec->toUnicode( replyData ); } diff --git a/src/dict/xdxf.cc b/src/dict/xdxf.cc index 2b7172608..f87b7660a 100644 --- a/src/dict/xdxf.cc +++ b/src/dict/xdxf.cc @@ -72,7 +72,7 @@ DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex ) enum { Signature = 0x46584458, // XDXF on little-endian, FXDX on big-endian - CurrentFormatVersion = 6 + BtreeIndexing::FormatVersion + Folding::Version + CurrentFormatVersion = 6 + BtreeIndexing::FormatVersion + Folding::Version + BtreeIndexing::ZipParseLogicVersion }; enum ArticleFormat { diff --git a/src/dict/zipsounds.cc b/src/dict/zipsounds.cc index 50d6f4445..2cf4dc5bb 100644 --- a/src/dict/zipsounds.cc +++ b/src/dict/zipsounds.cc @@ -37,7 +37,7 @@ DEF_EX( exInvalidData, "Invalid data encountered", Dictionary::Ex ) enum { Signature = 0x5350495a, // ZIPS on little-endian, SPIZ on big-endian - CurrentFormatVersion = 6 + BtreeIndexing::FormatVersion + CurrentFormatVersion = 6 + BtreeIndexing::FormatVersion + BtreeIndexing::ZipParseLogicVersion }; #pragma pack( push, 1 ) @@ -216,7 +216,7 @@ sptr< Dictionary::DataRequest > ZipSoundsDictionary::getArticle( std::u32string multimap< std::u32string, uint32_t >::const_iterator i; - result += ""; + result += "
"; vector< char > chunk; char * nameBlock; @@ -247,7 +247,7 @@ sptr< Dictionary::DataRequest > ZipSoundsDictionary::getArticle( std::u32string string displayedName = mainArticles.size() + alternateArticles.size() > 1 ? name : Text::toUtf8( stripExtension( name ) ); - result += "
"; + result += "
"; QUrl url; url.setScheme( "gdau" ); @@ -258,9 +258,9 @@ sptr< Dictionary::DataRequest > ZipSoundsDictionary::getArticle( std::u32string result += addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += ""; + result += "Play)"; + result += "" + Html::escape( displayedName ) + ""; + result += ""; } for ( i = alternateArticles.begin(); i != alternateArticles.end(); ++i ) { @@ -289,7 +289,7 @@ sptr< Dictionary::DataRequest > ZipSoundsDictionary::getArticle( std::u32string string displayedName = mainArticles.size() + alternateArticles.size() > 1 ? name : Text::toUtf8( stripExtension( name ) ); - result += ""; + result += "
"; QUrl url; url.setScheme( "gdau" ); @@ -298,14 +298,14 @@ sptr< Dictionary::DataRequest > ZipSoundsDictionary::getArticle( std::u32string string ref = string( "\"" ) + url.toEncoded().data() + "\""; - result += addAudioLink( url.toEncoded(), getId() ); + addAudioLink( url.toEncoded(), getId() ); - result += "
)"; - result += ""; - result += ""; + result += "Play)"; + result += "" + Html::escape( displayedName ) + ""; + result += ""; } - result += "
Play" + Html::escape( displayedName ) + "
Play" + Html::escape( displayedName ) + "
"; + result += ""; auto ret = std::make_shared< Dictionary::DataRequestInstant >( true ); ret->appendString( result ); diff --git a/src/iframeschemehandler.cc b/src/iframeschemehandler.cc index 9bf6d6770..3731a8d0f 100644 --- a/src/iframeschemehandler.cc +++ b/src/iframeschemehandler.cc @@ -1,6 +1,6 @@ #include "iframeschemehandler.hh" -#include +#include "iconv.hh" IframeSchemeHandler::IframeSchemeHandler( QObject * parent ): QWebEngineUrlSchemeHandler( parent ) @@ -36,9 +36,9 @@ void IframeSchemeHandler::requestStarted( QWebEngineUrlRequestJob * requestJob ) QByteArray replyData = reply->readAll(); QString articleString; - QTextCodec * codec = QTextCodec::codecForUtfText( replyData, QTextCodec::codecForName( codecName.toUtf8() ) ); - if ( codec ) { - articleString = codec->toUnicode( replyData ); + auto encoding = Iconv::findValidEncoding( { codecName } ); + if ( !encoding.isEmpty() ) { + articleString = Iconv::toQString( encoding.toUtf8().constData(), replyData.data(), replyData.size() ); } else { articleString = QString::fromUtf8( replyData ); diff --git a/src/main.cc b/src/main.cc index 36a3718f5..2ce5bd93d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -348,7 +348,10 @@ int main( int argc, char ** argv ) //high dpi screen support - qputenv( "QT_ENABLE_HIGHDPI_SCALING", "1" ); + if ( !qEnvironmentVariableIsSet( "QT_ENABLE_HIGHDPI_SCALING" ) + || qEnvironmentVariableIsEmpty( "QT_ENABLE_HIGHDPI_SCALING" ) ) { + qputenv( "QT_ENABLE_HIGHDPI_SCALING", "1" ); + } QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::PassThrough ); QHotkeyApplication app( "GoldenDict-ng", argc, argv ); diff --git a/src/stylesheets/article-style-st-classic.css b/src/stylesheets/article-style-st-classic.css index f51feedd8..043987cc9 100644 --- a/src/stylesheets/article-style-st-classic.css +++ b/src/stylesheets/article-style-st-classic.css @@ -323,16 +323,6 @@ div.xdxf { /************* LSA audio archives **************/ -/* A table which contains a play icon and a word's link */ -.lsa_play { - margin-top: 8px; - margin-left: 8px; -} - -.lsa_play a { - text-decoration: none; -} - /************* DSL dictionaries **************/ .dsl_u { @@ -593,34 +583,6 @@ div.xdxf { color: red; } -/************* Programs **************/ - -/* A table which contains a play icon and a word's link */ -.programs_play { - margin-top: 8px; - margin-left: 8px; -} - -.programs_play a { - text-decoration: none; -} - -.programs_plaintext, -.programs_html { - margin-top: 15px; -} - -/************* Voice engines **************/ - -.voiceengines_play { - margin-top: 8px; - margin-left: 8px; -} - -.voiceengines_play a { - text-decoration: none; -} - /************* MediaWiki articles ***************** The following consist of excerpts from different .css files edited with a .mwiki prepended to each record. diff --git a/src/stylesheets/article-style.css b/src/stylesheets/article-style.css index 2dc2c80af..21516bf1e 100644 --- a/src/stylesheets/article-style.css +++ b/src/stylesheets/article-style.css @@ -376,16 +376,28 @@ div.xdxf { font-weight: bold; } -/************* LSA audio archives **************/ +/************* audio archives **************/ -/* A table which contains a play icon and a word's link */ -.lsa_play { - margin-top: 8px; - margin-left: 8px; +.audio-play { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 10px; } - -.lsa_play a { +.audio-play-item { + display: flex; + align-items: center; +} +.audio-play-item a { text-decoration: none; + color: inherit; + margin-right: 10px; +} +.audio-play-item img { + border: 0; + vertical-align: middle; + width: 24px; + height: 24px; } /************* DSL dictionaries **************/ @@ -674,34 +686,6 @@ div.xdxf { color: red; } -/************* Programs **************/ - -/* A table which contains a play icon and a word's link */ -.programs_play { - margin-top: 8px; - margin-left: 8px; -} - -.programs_play a { - text-decoration: none; -} - -.programs_plaintext, -.programs_html { - margin-top: 15px; -} - -/************* Voice engines **************/ - -.voiceengines_play { - margin-top: 8px; - margin-left: 8px; -} - -.voiceengines_play a { - text-decoration: none; -} - /************* MediaWiki articles ***************** The following consist of excerpts from different .css files edited with a .mwiki prepended to each record. @@ -718,6 +702,12 @@ div.xdxf { display: none; } +/* Hide empty list items */ +.mwiki .mw-empty-li, +.mwiki .mw-empty-elt { + display: none; +} + /************ common.css **************/ /** diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index b1e8d30b1..aca6fea7b 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -821,12 +821,13 @@ void ArticleView::linkHovered( const QString & link ) if ( url.scheme() == "bres" ) { msg = tr( "Resource" ); } - else if ( url.scheme() == "gdau" || Utils::Url::isAudioUrl( url ) ) { - msg = tr( "Audio" ); - } else if ( url.scheme() == "gdtts" ) { msg = tr( "TTS Voice" ); } + else if ( url.scheme() == "gdau" || Utils::Url::isAudioUrl( url ) ) { + msg = tr( "Audio" ); + } + else if ( url.scheme() == "gdvideo" ) { if ( url.path().isEmpty() ) { msg = tr( "Video" ); @@ -994,87 +995,8 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const & } else if ( url.scheme() == "bres" || url.scheme() == "gdau" || url.scheme() == "gdvideo" || Utils::Url::isAudioUrl( url ) ) { - // Download it - - if ( Utils::Url::isWebAudioUrl( url ) ) { - sptr< Dictionary::DataRequest > req = std::make_shared< Dictionary::WebMultimediaDownload >( url, articleNetMgr ); - - connect( req.get(), &Dictionary::Request::finished, this, [ req, url, this ]() { - resourceDownloadFinished( req, url ); - } ); - } - else { - // Normal resource download - QString contentType; - - sptr< Dictionary::DataRequest > req = articleNetMgr.getResource( url, contentType ); - - if ( !req.get() ) { - qDebug() << "request failed: " << url; - // Request failed, fail - } - else if ( req->isFinished() && req->dataSize() >= 0 ) { - // Have data ready, handle it - resourceDownloadFinished( req, url ); - - return; - } - else if ( !req->isFinished() ) { - connect( req.get(), &Dictionary::Request::finished, this, [ req, url, this ]() { - resourceDownloadFinished( req, url ); - } ); - } - } + playAudio( url ); } - else if ( url.scheme() == "gdprg" ) { - // Program. Run it. - QString id( url.host() ); - - for ( const auto & program : cfg.programs ) { - if ( program.id == id ) { - // Found the corresponding program. - Programs::RunInstance * req = new Programs::RunInstance; - - connect( req, &Programs::RunInstance::finished, req, &QObject::deleteLater ); - - QString error; - - // Delete the request if it fails to start - if ( !req->start( program, url.path().mid( 1 ), error ) ) { - delete req; - - QMessageBox::critical( this, "GoldenDict", error ); - } - - return; - } - } - - // Still here? No such program exists. - QMessageBox::critical( this, "GoldenDict", tr( "The referenced audio program doesn't exist." ) ); - } - else if ( url.scheme() == "gdtts" ) { -#ifdef TTS_SUPPORT - // Text to speech - QString md5Id = Utils::Url::queryItemValue( url, "engine" ); - QString text( url.path().mid( 1 ) ); - - for ( const auto & voiceEngine : cfg.voiceEngines ) { - QString itemMd5Id = - QString( QCryptographicHash::hash( voiceEngine.name.toUtf8(), QCryptographicHash::Md5 ).toHex() ); - - if ( itemMd5Id == md5Id ) { - SpeechClient * speechClient = new SpeechClient( voiceEngine, this ); - connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) ); - speechClient->tell( text ); - break; - } - } -#else - qDebug() << "gdtts:// is not supported due to missing TTS support"; -#endif - } - else if ( Utils::isExternalLink( url ) ) { // Use the system handler for the conventional external links QDesktopServices::openUrl( url ); @@ -1124,6 +1046,54 @@ void ArticleView::playAudio( QUrl const & url ) } } } + else if ( url.scheme() == "gdprg" ) { + // Program. Run it. + QString id( url.host() ); + + for ( const auto & program : cfg.programs ) { + if ( program.id == id ) { + // Found the corresponding program. + Programs::RunInstance * req = new Programs::RunInstance; + + connect( req, &Programs::RunInstance::finished, req, &QObject::deleteLater ); + + QString error; + + // Delete the request if it fails to start + if ( !req->start( program, url.path().mid( 1 ), error ) ) { + delete req; + + QMessageBox::critical( this, "GoldenDict", error ); + } + + return; + } + } + + // Still here? No such program exists. + QMessageBox::critical( this, "GoldenDict", tr( "The referenced audio program doesn't exist." ) ); + } + else if ( url.scheme() == "gdtts" ) { +#ifdef TTS_SUPPORT + // Text to speech + QString md5Id = Utils::Url::queryItemValue( url, "engine" ); + QString text( url.path().mid( 1 ) ); + + for ( const auto & voiceEngine : cfg.voiceEngines ) { + QString itemMd5Id = + QString( QCryptographicHash::hash( voiceEngine.name.toUtf8(), QCryptographicHash::Md5 ).toHex() ); + + if ( itemMd5Id == md5Id ) { + SpeechClient * speechClient = new SpeechClient( voiceEngine, this ); + connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) ); + speechClient->tell( text ); + break; + } + } +#else + qDebug() << "gdtts:// is not supported due to missing TTS support"; +#endif + } } } diff --git a/src/ui/favoritespanewidget.cc b/src/ui/favoritespanewidget.cc index 897f099b0..985fe9959 100644 --- a/src/ui/favoritespanewidget.cc +++ b/src/ui/favoritespanewidget.cc @@ -52,6 +52,10 @@ void FavoritesPaneWidget::setUp( Config::Class * cfg, QMenu * menu ) addAction( m_addFolder ); connect( m_addFolder, &QAction::triggered, this, &FavoritesPaneWidget::addFolder ); + m_clearAll = new QAction( this ); + m_clearAll->setText( tr( "Clear All" ) ); + addAction( m_clearAll ); + connect( m_clearAll, &QAction::triggered, this, &FavoritesPaneWidget::clearAllItems ); // Handle context menu, reusing some of the top-level window's History menu m_favoritesMenu = new QMenu( this ); @@ -182,6 +186,7 @@ void FavoritesPaneWidget::showCustomMenu( QPoint const & pos ) m_favoritesMenu->removeAction( m_copySelectedToClipboard ); m_favoritesMenu->removeAction( m_deleteSelectedAction ); m_favoritesMenu->removeAction( m_addFolder ); + m_favoritesMenu->removeAction( m_clearAll ); m_separator->setVisible( !selectedIdxs.isEmpty() ); @@ -192,6 +197,7 @@ void FavoritesPaneWidget::showCustomMenu( QPoint const & pos ) if ( selectedIdxs.size() <= 1 ) { m_favoritesMenu->insertAction( m_separator, m_addFolder ); + m_favoritesMenu->insertAction( m_separator, m_clearAll ); m_separator->setVisible( true ); } @@ -253,6 +259,18 @@ void FavoritesPaneWidget::addFolder() } } +void FavoritesPaneWidget::clearAllItems() +{ + QMessageBox::StandardButton reply; + reply = QMessageBox::question( this, + tr( "Clear All Items" ), + tr( "Are you sure you want to clear all items?" ), + QMessageBox::Yes | QMessageBox::No ); + if ( reply == QMessageBox::Yes ) { + m_favoritesModel->clearAllItems(); + } +} + void FavoritesPaneWidget::addHeadword( QString const & path, QString const & headword ) { m_favoritesModel->addNewHeadword( path, headword ); @@ -325,8 +343,14 @@ TreeItem::TreeItem( const QVariant & data, TreeItem * parent, Type type ): } TreeItem::~TreeItem() +{ + clearChildren(); +} + +void TreeItem::clearChildren() { qDeleteAll( childItems ); + childItems.clear(); } void TreeItem::appendChild( TreeItem * item ) @@ -721,13 +745,20 @@ void FavoritesModel::addFolder( TreeItem * parent, QDomNode & node ) if ( el.nodeName() == "folder" ) { // New subfolder QString name = el.attribute( "name", "" ); - TreeItem * item = new TreeItem( name, parent, TreeItem::Folder ); - item->setExpanded( el.attribute( "expanded", "0" ) == "1" ); - parent->appendChild( item ); + TreeItem * existingItem = findFolderByName( parent, name, TreeItem::Folder ); + TreeItem * item = existingItem != nullptr ? existingItem : new TreeItem( name, parent, TreeItem::Folder ); + if ( existingItem == nullptr ) { + item->setExpanded( el.attribute( "expanded", "0" ) == "1" ); + parent->appendChild( item ); + } addFolder( item, el ); } else { QString word = el.text(); + TreeItem * existingItem = findFolderByName( parent, word, TreeItem::Word ); + if ( existingItem != nullptr ) { + continue; + } parent->appendChild( new TreeItem( word, parent, TreeItem::Word ) ); GlobalBroadcaster::instance()->folderFavoritesMap[ parent->data().toString() ].insert( word ); @@ -869,6 +900,17 @@ QModelIndex FavoritesModel::findItemInFolder( const QString & itemName, int item return QModelIndex(); } +TreeItem * FavoritesModel::findFolderByName( TreeItem * parent, const QString & name, TreeItem::Type type ) +{ + for ( int i = 0; i < parent->childCount(); i++ ) { + TreeItem * child = parent->child( i ); + if ( child->type() == type && child->data().toString() == name ) { + return child; + } + } + return nullptr; +} + TreeItem * FavoritesModel::getItem( const QModelIndex & index ) const { if ( index.isValid() ) { @@ -1145,12 +1187,10 @@ bool FavoritesModel::setDataFromXml( QString const & dataStr ) beginResetModel(); - if ( rootItem ) { - delete rootItem; + if ( !rootItem ) { + rootItem = new TreeItem( QVariant(), 0, TreeItem::Root ); } - rootItem = new TreeItem( QVariant(), 0, TreeItem::Root ); - QDomNode rootNode = dom.documentElement(); addFolder( rootItem, rootNode ); @@ -1167,12 +1207,10 @@ bool FavoritesModel::setDataFromTxt( QString const & dataStr ) beginResetModel(); - if ( rootItem ) { - delete rootItem; + if ( !rootItem ) { + rootItem = new TreeItem( QVariant(), 0, TreeItem::Root ); } - rootItem = new TreeItem( QVariant(), 0, TreeItem::Root ); - for ( auto const & word : words ) { rootItem->appendChild( new TreeItem( word, rootItem, TreeItem::Word ) ); } @@ -1181,3 +1219,13 @@ bool FavoritesModel::setDataFromTxt( QString const & dataStr ) dirty = true; return true; } +void FavoritesModel::clearAllItems() +{ + beginResetModel(); + + if ( rootItem ) { + rootItem->clearChildren(); + } + + endResetModel(); +} diff --git a/src/ui/favoritespanewidget.hh b/src/ui/favoritespanewidget.hh index c1c202ced..96adb40f0 100644 --- a/src/ui/favoritespanewidget.hh +++ b/src/ui/favoritespanewidget.hh @@ -20,6 +20,7 @@ class FavoritesModel; +class TreeItem; class FavoritesPaneWidget: public QWidget { Q_OBJECT @@ -80,10 +81,10 @@ private slots: void deleteSelectedItems(); void copySelectedItems(); void addFolder(); + void clearAllItems(); private: virtual bool eventFilter( QObject *, QEvent * ); - Config::Class * m_cfg = nullptr; QTreeView * m_favoritesTree = nullptr; QMenu * m_favoritesMenu = nullptr; @@ -91,6 +92,7 @@ private: QAction * m_separator = nullptr; QAction * m_copySelectedToClipboard = nullptr; QAction * m_addFolder = nullptr; + QAction * m_clearAll = nullptr; QWidget favoritesPaneTitleBar; QHBoxLayout favoritesPaneTitleBarLayout; @@ -164,6 +166,7 @@ public: // Retrieve text from all childs QStringList getTextFromAllChilds() const; + void clearChildren(); private: QList< TreeItem * > childItems; @@ -180,6 +183,7 @@ class FavoritesModel: public QAbstractItemModel public: explicit FavoritesModel( QString favoritesFilename, QObject * parent = 0 ); ~FavoritesModel(); + void clearAllItems(); QVariant data( const QModelIndex & index, int role ) const; Qt::ItemFlags flags( const QModelIndex & index ) const; @@ -249,6 +253,7 @@ protected: void readData(); void addFolder( TreeItem * parent, QDomNode & node ); void storeFolder( TreeItem * folder, QDomNode & node ); + TreeItem * findFolderByName( TreeItem * parent, const QString & name, TreeItem::Type type ); // Find item in folder QModelIndex findItemInFolder( QString const & itemName, int itemType, QModelIndex const & parentIdx ); diff --git a/src/ui/mainwindow.cc b/src/ui/mainwindow.cc index 4f6174b37..67d16d195 100644 --- a/src/ui/mainwindow.cc +++ b/src/ui/mainwindow.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include "weburlrequestinterceptor.hh" #include "folding.hh" @@ -241,14 +242,12 @@ MainWindow::MainWindow( Config::Class & cfg_ ): // translate box groupListInToolbar = new GroupComboBox( navToolbar ); - groupListInToolbar->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); + groupListInToolbar->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::MinimumExpanding ); groupListInToolbar->setSizeAdjustPolicy( QComboBox::AdjustToContents ); - groupListInToolbar->setStyleSheet( "QComboBox { padding: 0px; margin: 0px; }" ); translateBoxLayout->addWidget( groupListInToolbar ); translateBox = new TranslateBox( navToolbar ); - translateBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); - translateBox->setStyleSheet( "QComboBox { padding: 0px; margin: 0px; }" ); + translateBox->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::MinimumExpanding ); translateBoxLayout->addWidget( translateBox ); translateBoxToolBarAction = navToolbar->addWidget( translateBoxWidget ); @@ -1508,12 +1507,7 @@ void MainWindow::quitApp() void MainWindow::applyProxySettings() { if ( cfg.preferences.proxyServer.enabled && cfg.preferences.proxyServer.useSystemProxy ) { - QList< QNetworkProxy > proxies = QNetworkProxyFactory::systemProxyForQuery(); - if ( !cfg.preferences.proxyServer.systemProxyUser.isEmpty() ) { - proxies.first().setUser( cfg.preferences.proxyServer.systemProxyUser ); - proxies.first().setPassword( cfg.preferences.proxyServer.systemProxyPassword ); - } - QNetworkProxy::setApplicationProxy( proxies.first() ); + QNetworkProxyFactory::setUseSystemConfiguration( true ); return; } @@ -2285,9 +2279,6 @@ void MainWindow::editPreferences() p.searchInDock = cfg.preferences.searchInDock; p.alwaysOnTop = cfg.preferences.alwaysOnTop; - p.proxyServer.systemProxyUser = cfg.preferences.proxyServer.systemProxyUser; - p.proxyServer.systemProxyPassword = cfg.preferences.proxyServer.systemProxyPassword; - p.fts.dialogGeometry = cfg.preferences.fts.dialogGeometry; p.fts.searchMode = cfg.preferences.fts.searchMode; @@ -4257,47 +4248,21 @@ void MainWindow::storeResourceSavePath( const QString & newPath ) void MainWindow::proxyAuthentication( const QNetworkProxy &, QAuthenticator * authenticator ) { + qDebug() << "Proxy Authentication Required"; QNetworkProxy proxy = QNetworkProxy::applicationProxy(); - QString *userStr, *passwordStr; - if ( cfg.preferences.proxyServer.useSystemProxy ) { - userStr = &cfg.preferences.proxyServer.systemProxyUser; - passwordStr = &cfg.preferences.proxyServer.systemProxyPassword; + if ( proxy.type() == QNetworkProxy::DefaultProxy ) { + qDebug() << "Current proxy is the system proxy."; } else { - userStr = &cfg.preferences.proxyServer.user; - passwordStr = &cfg.preferences.proxyServer.password; - } - - if ( proxy.user().isEmpty() && !userStr->isEmpty() ) { - authenticator->setUser( *userStr ); - authenticator->setPassword( *passwordStr ); - - proxy.setUser( *userStr ); - proxy.setPassword( *passwordStr ); - QNetworkProxy::setApplicationProxy( proxy ); + qDebug() << "Current proxy is not the system proxy."; } - else { - QDialog dlg; - Ui::Dialog ui; - ui.setupUi( &dlg ); - dlg.adjustSize(); - ui.userEdit->setText( *userStr ); - ui.passwordEdit->setText( *passwordStr ); - - if ( dlg.exec() == QDialog::Accepted ) { - *userStr = ui.userEdit->text(); - *passwordStr = ui.passwordEdit->text(); - - authenticator->setUser( *userStr ); - authenticator->setPassword( *passwordStr ); - - proxy.setUser( *userStr ); - proxy.setPassword( *passwordStr ); - QNetworkProxy::setApplicationProxy( proxy ); - } - } + qDebug() << "Proxy Type:" << proxy.type(); + qDebug() << "Proxy Host Name:" << proxy.hostName(); + qDebug() << "Proxy Port:" << proxy.port(); + qDebug() << "Proxy User:" << proxy.user(); + qDebug() << "Proxy Password:" << ( proxy.password().isEmpty() ? "Not set" : "Set" ); } void MainWindow::showFullTextSearchDialog() diff --git a/src/ui/preferences.ui b/src/ui/preferences.ui index 6c2bba015..c9c35ccd9 100644 --- a/src/ui/preferences.ui +++ b/src/ui/preferences.ui @@ -1042,9 +1042,6 @@ for all program's network requests.
- - false - Host: diff --git a/src/ui/translatebox.cc b/src/ui/translatebox.cc index 37ea4b64e..1066a99b5 100644 --- a/src/ui/translatebox.cc +++ b/src/ui/translatebox.cc @@ -21,9 +21,7 @@ TranslateBox::TranslateBox( QWidget * parent ): { completer = new QCompleter( words, this ); resize( 200, 90 ); - QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); - sizePolicy.setHorizontalStretch( 0 ); - sizePolicy.setVerticalStretch( 0 ); + QSizePolicy sizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); setSizePolicy( sizePolicy ); setFocusProxy( translate_line ); diff --git a/website/docs/install.md b/website/docs/install.md index 9feedcb1f..eb33be22c 100644 --- a/website/docs/install.md +++ b/website/docs/install.md @@ -20,7 +20,8 @@ Choose either If Qt's version is not changed, you can also download a single `goldendict.exe` and drop it into previous installation's folder (If uncertain, don't do this). -Requires Windows 10 (1809 or later). +Requires Windows 10 (1809 or later) with [MSVC runtime](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-microsoft-visual-c-redistributable-version) installed. + ## Linux @@ -43,4 +44,4 @@ Requires at least macOS 13. This project uses Calendar Versioning: `YY.MM.Patch`. -Releases will tentatively be done twice a year, considering factors like the major releases of Qt and the package freeze dates of Linux distros like Ubuntu. \ No newline at end of file +Releases will tentatively be done twice a year, considering factors like the major releases of Qt and the package freeze dates of Linux distros like Ubuntu. diff --git a/website/docs/manage_sources.md b/website/docs/manage_sources.md index df10a3244..f571f66a2 100644 --- a/website/docs/manage_sources.md +++ b/website/docs/manage_sources.md @@ -53,12 +53,7 @@ Target word can be inserted into url in next encodings:: | Target word template | Encoding | |------------------------|-----------------------------------------| | %GDWORD% | UTF-8 | -| %GD1251% | Windows-1251 | -| %GDISO1% ... %GDISO16% | ISO 8859-1 ... ISO 8859-16 respectively | -| %GDBIG5% | Big-5 | -| %GDBIG5HKSCS% | Big5-HKSCS | -| %GDGBK% | GBK and GB 18030 | -| %GDSHIFTJIS% | Shift-JIS | + ## DICT servers diff --git a/website/docs/topic_userstyle.md b/website/docs/topic_userstyle.md index 16d08405a..c409342d9 100644 --- a/website/docs/topic_userstyle.md +++ b/website/docs/topic_userstyle.md @@ -19,6 +19,10 @@ DarkReader.enable({ contrast: 100, sepia: 0, grayscale: 0, + darkSchemeBackgroundColor: "#181a1b", + darkSchemeTextColor: "#e8e6e3", + lightSchemeBackgroundColor: "#dcdad7", + lightSchemeTextColor: "#181a1b", }); ```