diff --git a/.github/workflows/release-windows-vcpkg-cmake.yml b/.github/workflows/release-windows-vcpkg-cmake.yml index 29c68c120..a909a0fa5 100644 --- a/.github/workflows/release-windows-vcpkg-cmake.yml +++ b/.github/workflows/release-windows-vcpkg-cmake.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: os: [windows-2022] - qt_ver: [ 6.7.2 ] + qt_ver: [ 6.7.2, 6.6.3 ] qt_arch: [win64_msvc2019_64] env: version: 24.05.13 @@ -133,6 +133,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | $tagName = "v$env:version-$env:versionSuffix.$(git rev-parse --short=8 HEAD)" + $releaseName = "v$env:version-$env:versionSuffix.$(date +'%y%m%d').$(git rev-parse --short=8 HEAD)" Add-Content -Path ./change_note.txt -Value " [Install instructions for Windows, macOS and Linux](https://xiaoyifang.github.io/goldendict-ng/install/). @@ -153,9 +154,9 @@ jobs: $tagExist = gh api --silent "repos/:owner/:repo/git/refs/tags/${tagName}" if (-not $?) { if ($env:prerelease -eq "true") { - gh release create ${tagName} --target ${{github.ref_name}} --notes-file=./change_note.txt --latest=false --prerelease + gh release create ${tagName} -t ${releaseName} --target ${{github.ref_name}} --notes-file=./change_note.txt --latest=false --prerelease } else { - gh release create ${tagName} --target ${{github.ref_name}} --notes-file=./change_note.txt --latest=true + gh release create ${tagName} -t ${releaseName} --target ${{github.ref_name}} --notes-file=./change_note.txt --latest=true } } @@ -167,8 +168,22 @@ jobs: gh release upload "${tagName}" "${namePrefix}.7z#${namePrefix}-Windows.7z" --clobber gh release upload "${tagName}" "${namePrefix}.exe#${namePrefix}-Windows-installer.exe" --clobber - cd './goldendict' - gh release upload "${tagName}" "goldendict.exe#${namePrefix}-Windows-main-exe-file-only.exe" --clobber - gh release upload "${tagName}" "goldendict.pdb#${namePrefix}-Windows-debug-file.pdb" --clobber - cd .. + - name: Upload Single packages + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + tagName="v${{env.version}}-${{env.versionSuffix}}.$(git rev-parse --short=8 HEAD)" + echo $tagName + namePrefix="GoldenDict-ng-${{env.version}}-Qt${{matrix.qt_ver}}" + + cd ./build_dir/goldendict + + # rename to avoid conflict with other packages + mv goldendict.exe goldendict-Qt${{matrix.qt_ver}}.exe + mv goldendict.pdb goldendict-Qt${{matrix.qt_ver}}.pdb + + gh release upload "${tagName}" "goldendict-Qt${{matrix.qt_ver}}.exe#${namePrefix}-Windows-main-exe-file-only.exe" --clobber + gh release upload "${tagName}" "goldendict-Qt${{matrix.qt_ver}}.pdb#${namePrefix}-Windows-debug-file.pdb" --clobber + cd .. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 29327d38b..267ad144b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,7 @@ set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/src/ui/") # https://cmake.org/cmake/help/latest/command/file.html#filesystem # ! Using GLOB_RECURSE is not recommended by cmake's documentation # CONFIGURE_DEPENDS will trigger file tree recheck in every rebuilds. -file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS src/*.cc src/*.hh src/*.c) +file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS src/*.cc src/*.hh src/*.ui src/*.c) if (APPLE) file(GLOB_RECURSE MACOS_SOURCE_FILES CONFIGURE_DEPENDS src/macos/*.mm) diff --git a/icons/lsasound.png b/icons/lsasound.png new file mode 100644 index 000000000..2d73561b4 Binary files /dev/null and b/icons/lsasound.png differ diff --git a/icons/lsasound.svg b/icons/lsasound.svg index 58d761c67..f1a4a4be9 100644 --- a/icons/lsasound.svg +++ b/icons/lsasound.svg @@ -3,14 +3,14 @@ lsa + id="g1">lsa diff --git a/resources.qrc b/resources.qrc index 7c49291da..132967e43 100644 --- a/resources.qrc +++ b/resources.qrc @@ -68,7 +68,7 @@ icons/playsound_color.svg icons/sounddir.svg icons/zipsound.svg - icons/lsasound.svg + icons/lsasound.png icons/previous.svg icons/print.svg icons/programs.svg diff --git a/src/btreeidx.cc b/src/btreeidx.cc index 856ca3afb..efad109d3 100644 --- a/src/btreeidx.cc +++ b/src/btreeidx.cc @@ -1097,27 +1097,6 @@ void BtreeIndex::findArticleLinks( QVector< WordArticleLink > * articleLinks, for ( ;; ) { vector< WordArticleLink > result = readChain( chainPtr ); - if ( headwords - && static_cast< vector< WordArticleLink >::size_type >( headwords->capacity() ) - < headwords->size() + result.size() ) { - int n = headwords->capacity(); - headwords->reserve( n + n / 10 ); - } - - if ( offsets - && static_cast< vector< WordArticleLink >::size_type >( offsets->capacity() ) - < offsets->size() + result.size() ) { - int n = offsets->capacity(); - offsets->reserve( n + n / 10 ); - } - - if ( articleLinks - && static_cast< vector< WordArticleLink >::size_type >( articleLinks->capacity() ) - < articleLinks->size() + result.size() ) { - int n = articleLinks->capacity(); - articleLinks->reserve( n + n / 10 ); - } - for ( auto & i : result ) { if ( isCancelled && Utils::AtomicInt::loadAcquire( *isCancelled ) ) return; diff --git a/src/common/folding.cc b/src/common/folding.cc index bf81b631c..5b4122bde 100644 --- a/src/common/folding.cc +++ b/src/common/folding.cc @@ -154,7 +154,7 @@ wstring applyWhitespaceAndPunctOnly( wstring const & in ) out.reserve( in.size() ); for ( size_t left = in.size(); left--; ++nextChar ) { - if ( !isWhitespace( *nextChar ) && !isPunct( *nextChar ) ) + if ( !isWhitespaceOrPunct( *nextChar ) ) out.push_back( *nextChar ); } @@ -163,12 +163,13 @@ wstring applyWhitespaceAndPunctOnly( wstring const & in ) bool isWhitespace( wchar ch ) { - return QChar::isSpace( ch ); + //invisible character should be treated as whitespace as well. + return QChar::isSpace( ch ) || !QChar::isPrint( ch ); } bool isWhitespaceOrPunct( wchar ch ) { - return QChar::isSpace( ch ) || QChar::isPunct( ch ); + return isWhitespace( ch ) || QChar::isPunct( ch ); } bool isPunct( wchar ch ) @@ -182,14 +183,13 @@ wstring trimWhitespaceOrPunct( wstring const & in ) wstring::size_type wordSize = in.size(); // Skip any leading whitespace - while ( *wordBegin && ( Folding::isWhitespace( *wordBegin ) || Folding::isPunct( *wordBegin ) ) ) { + while ( *wordBegin && Folding::isWhitespaceOrPunct( *wordBegin ) ) { ++wordBegin; --wordSize; } // Skip any trailing whitespace - while ( wordSize - && ( Folding::isWhitespace( wordBegin[ wordSize - 1 ] ) || Folding::isPunct( wordBegin[ wordSize - 1 ] ) ) ) + while ( wordSize && Folding::isWhitespaceOrPunct( wordBegin[ wordSize - 1 ] ) ) --wordSize; return wstring( wordBegin, wordSize ); diff --git a/src/common/globalregex.hh b/src/common/globalregex.hh index 7498e80e1..b3e1a1269 100644 --- a/src/common/globalregex.hh +++ b/src/common/globalregex.hh @@ -69,10 +69,11 @@ bool containHtmlEntity( std::string const & text ); } // namespace Html const static QRegularExpression accentMark( R"(\p{M})", QRegularExpression::UseUnicodePropertiesOption ); -//contain unicode space mark and punctuation -const static QRegularExpression markPuncSpace( R"([\p{M}\p{Z}\p{P}])", QRegularExpression::UseUnicodePropertiesOption ); -//contain unicode space and mark. -const static QRegularExpression markSpace( R"([\p{M}\p{Z}])", QRegularExpression::UseUnicodePropertiesOption ); +//contain unicode space mark,invisible, and punctuation +const static QRegularExpression markPuncSpace( R"([\p{M}\p{Z}\p{C}\p{P}])", + QRegularExpression::UseUnicodePropertiesOption ); +//contain unicode space and mark.invisible +const static QRegularExpression markSpace( R"([\p{M}\p{Z}\p{C}])", QRegularExpression::UseUnicodePropertiesOption ); const static QRegularExpression whiteSpace( "\\s+" ); diff --git a/src/dict/dsl.cc b/src/dict/dsl.cc index 83ae04dec..91d81f7a2 100644 --- a/src/dict/dsl.cc +++ b/src/dict/dsl.cc @@ -1565,7 +1565,7 @@ void DslResourceRequest::run() string n = dict.getContainingFolder().toStdString() + Utils::Fs::separator() + resourceName; - GD_DPRINTF( "n is %s\n", n.c_str() ); + GD_DPRINTF( "dsl resource name is %s\n", n.c_str() ); try { try { diff --git a/src/dict/gls.cc b/src/dict/gls.cc index 8f20a9154..2b96ae500 100644 --- a/src/dict/gls.cc +++ b/src/dict/gls.cc @@ -1069,7 +1069,7 @@ void GlsResourceRequest::run() try { string n = dict.getContainingFolder().toStdString() + Utils::Fs::separator() + resourceName; - GD_DPRINTF( "n is %s\n", n.c_str() ); + GD_DPRINTF( "gls resource name is %s\n", n.c_str() ); try { QMutexLocker _( &dataMutex ); diff --git a/src/dict/lsa.cc b/src/dict/lsa.cc index 644da7652..4874a97fe 100644 --- a/src/dict/lsa.cc +++ b/src/dict/lsa.cc @@ -483,7 +483,7 @@ void LsaDictionary::loadIcon() noexcept if ( !loadIconFromFile( fileName ) ) { // Load failed -- use default icons - dictionaryIcon = QIcon( ":/icons/lsasound.svg" ); + dictionaryIcon = QIcon( ":/icons/lsasound.png" ); } dictionaryIconLoaded = true; diff --git a/src/dict/stardict.cc b/src/dict/stardict.cc index d2bfcbaf6..468626413 100644 --- a/src/dict/stardict.cc +++ b/src/dict/stardict.cc @@ -1517,7 +1517,7 @@ void StardictResourceRequest::run() string n = dict.getContainingFolder().toStdString() + Utils::Fs::separator() + "res" + Utils::Fs::separator() + resourceName; - GD_DPRINTF( "n is %s\n", n.c_str() ); + GD_DPRINTF( "startdict resource name is %s\n", n.c_str() ); try { QMutexLocker _( &dataMutex ); diff --git a/src/dict/xdxf.cc b/src/dict/xdxf.cc index 3c491a15c..79e01e218 100644 --- a/src/dict/xdxf.cc +++ b/src/dict/xdxf.cc @@ -942,7 +942,7 @@ void XdxfResourceRequest::run() string n = dict.getContainingFolder().toStdString() + Utils::Fs::separator() + resourceName; - GD_DPRINTF( "n is %s\n", n.c_str() ); + GD_DPRINTF( "xdxf resource name is %s\n", n.c_str() ); try { try { diff --git a/src/indexedzip.cc b/src/indexedzip.cc index 67e26c4df..4990d708f 100644 --- a/src/indexedzip.cc +++ b/src/indexedzip.cc @@ -13,6 +13,7 @@ #else #include #endif +#include using namespace BtreeIndexing; using std::vector; @@ -54,15 +55,23 @@ bool IndexedZip::loadFile( uint32_t offset, vector< char > & data ) if ( !zipIsOpen ) return false; + QMutexLocker _( &mutex ); // Now seek into the zip file and read its header - if ( !zip.seek( offset ) ) return false; ZipFile::LocalFileHeader header; if ( !ZipFile::readLocalHeader( zip, header ) ) { + vector< string > zipFileNames; + zip.getFilenames( zipFileNames ); GD_DPRINTF( "Failed to load header" ); + string filename; + if ( zip.getCurrentFile() < zipFileNames.size() ) { + filename = zipFileNames.at( zip.getCurrentFile() ); + } + + qDebug() << "Current failed zip file:" << QString::fromStdString( filename ); return false; } @@ -123,6 +132,8 @@ bool IndexedZip::indexFile( BtreeIndexing::IndexedWords & zipFileNames, quint32 { if ( !zipIsOpen ) return false; + + QMutexLocker _( &mutex ); if ( !ZipFile::positionAtCentralDir( zip ) ) return false; diff --git a/src/indexedzip.hh b/src/indexedzip.hh index d904a1702..204667205 100644 --- a/src/indexedzip.hh +++ b/src/indexedzip.hh @@ -7,6 +7,7 @@ #include "btreeidx.hh" #include #include "zipfile.hh" +#include /// Allows using a btree index to read zip files. Basically built on top of /// the base dictionary infrastructure adapted for zips. @@ -14,6 +15,7 @@ class IndexedZip: public BtreeIndexing::BtreeIndex { ZipFile::SplitZipFile zip; bool zipIsOpen; + QMutex mutex; public: diff --git a/src/splitfile.cc b/src/splitfile.cc index 55ca3a699..e1ed0c0d2 100644 --- a/src/splitfile.cc +++ b/src/splitfile.cc @@ -44,6 +44,11 @@ void SplitFile::getFilenames( vector< string > & names ) const names.push_back( ( *i )->fileName().toStdString() ); } +int SplitFile::getCurrentFile() const +{ + return currentFile; +} + bool SplitFile::open( QFile::OpenMode mode ) { for ( QVector< QFile * >::iterator i = files.begin(); i != files.end(); ++i ) diff --git a/src/splitfile.hh b/src/splitfile.hh index 2b308094f..3801e5f21 100644 --- a/src/splitfile.hh +++ b/src/splitfile.hh @@ -32,6 +32,7 @@ public: virtual void setFileName( const QString & name ) = 0; void getFilenames( vector< string > & names ) const; + int getCurrentFile() const; bool open( QFile::OpenMode mode ); void close(); bool seek( quint64 pos ); diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index 048fcc5a9..3c6d306ad 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -2026,8 +2026,13 @@ void ArticleView::performFindOperation( bool backwards ) f |= QWebEnginePage::FindBackward; findText( text, f, [ text, this ]( bool match ) { - bool setMark = !text.isEmpty() && !match; - Utils::Widget::setNoResultColor( searchPanel->lineEdit, setMark ); + bool nomatch = !text.isEmpty() && !match; + if ( nomatch ) { + //clear the previous findText results. + //when the results is empty, the highlight has not been removed.more likely a qt bug. + webview->findText( "" ); + } + Utils::Widget::setNoResultColor( searchPanel->lineEdit, nomatch ); } ); } @@ -2065,10 +2070,7 @@ bool ArticleView::closeSearch() ftsSearchPanel->hide(); webview->setFocus(); - QWebEnginePage::FindFlags flags( 0 ); - - webview->findText( "", flags ); - + webview->findText( "" ); return true; } return false; diff --git a/src/ui/mainwindow.cc b/src/ui/mainwindow.cc index 2e4a2af3e..e240d00c4 100644 --- a/src/ui/mainwindow.cc +++ b/src/ui/mainwindow.cc @@ -1535,7 +1535,7 @@ void MainWindow::makeDictionaries() ftsIndexing.doIndexing(); updateStatusLine(); - updateGroupList(); + updateGroupList( false ); } void MainWindow::updateStatusLine() @@ -1597,9 +1597,9 @@ void MainWindow::updateGroupList( bool reload ) updateDictionaryBar(); - qDebug() << "Reloading all the tabs..."; - if ( reload ) { + qDebug() << "Reloading all the tabs..."; + for ( int i = 0; i < ui.tabWidget->count(); ++i ) { auto & view = dynamic_cast< ArticleView & >( *( ui.tabWidget->widget( i ) ) ); diff --git a/website/docs/topic_gd-tools.md b/website/docs/topic_gd-tools.md new file mode 100644 index 000000000..f6a5146df --- /dev/null +++ b/website/docs/topic_gd-tools.md @@ -0,0 +1,28 @@ +# GoldenDict tools + +A set of helpful programs to enhance goldendict for immersion learning. + + +# prerequisite +1. install [gd-tools](https://codeberg.org/hashirama/gd-tools) and configure it according to its README + +# features: +- japanese sentence spliting, making each part of the sentence clickable +![Alt](https://codeberg.org/hashirama/gd-tools/raw/branch/main/misc/marisa.gif) + +## How to setup: +Open GoldenDict, press "Edit" > "Dictionaries" > "Programs" and add the installed executables. Set type to html. Command Line: gd-tools --word %GDWORD% --sentence %GDSEARCH%. Optionally add arguments, such as: gd-tools marisa --word %GDWORD% --sentence %GDSEARCH% . These programs are treated as dictionaries and you can add them under "Dictionaries" or "Groups". +

+please notice that gd-tools does works in windows, and we have an [installer](https://www.mediafire.com/file/h1v7owj7np9j7wg/gd-tools_windows.zip/file) for it, you can install and then come back to the previous instruction. +And if you're at Gnu Guix, install it from our [channel](https://codeberg.org/hashirama/ajattix)

+other features: +- kanji stroke order: for those who want to know how to write a character +- image searching +and much more, please see our list [here](https://codeberg.org/hashirama/gd-tools/src/branch/main/README.md#table-of-contents) + +# Misc +we have a mandarin version of gd-marisa, which relies on mecab (unix only) :

+![image](https://codeberg.org/hashirama/gd-tools/raw/branch/main/misc/mandarin.png) + +# Notes +This article was written by 柱間(developer of gd-tools). diff --git a/website/mkdocs.yml b/website/mkdocs.yml index 3f6b6910e..ed69f8378 100644 --- a/website/mkdocs.yml +++ b/website/mkdocs.yml @@ -37,17 +37,19 @@ nav: - Favorites: ui_favorites.md - Shortcuts: ui_shortcuts.md - Advanced Usages: - - Anki Integration: topic_anki.md - Program dictionary: howto/how to add a program as dictionary.md - Command Lines: topic_commandline.md - Custom Stylesheet & JavaScript: topic_userstyle.md - Portable Mode: topic_portablemode.md - Custom transliteration: topic_transliteration.md - Customize Dictionary: custom_dictionary.md - - OCR Integration: howto/ocr.md - Wayland: topic_wayland.md - Debug dictionary JS: howto/how to debug dictionary js.md - Flatpak/FlatHub: topic_flatpak.md + - Related tools: + - Anki Integration: topic_anki.md + - OCR Integration: howto/ocr.md + - gd-tools: topic_gd-tools.md - Report Bugs & Feedbacks: feedbacks.md - Development Info: - Start develop: developer.md