From a625bee4982fcd124d4e4ef07c3997bbcd0a7263 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 21 May 2022 09:08:46 +0800 Subject: [PATCH 001/125] bump alpha version to 22.5.22 --- .github/workflows/AutoTag.yml | 2 +- .github/workflows/macos-6.2.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/ubuntu-6.2.yml | 2 +- .github/workflows/ubuntu-PR-check.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/windows-6.2.yml | 2 +- .github/workflows/windows-PR-check.yml | 2 +- .github/workflows/windows.yml | 2 +- goldendict.pro | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/AutoTag.yml b/.github/workflows/AutoTag.yml index eb7d9863..b2ff9516 100644 --- a/.github/workflows/AutoTag.yml +++ b/.github/workflows/AutoTag.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest env: - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true diff --git a/.github/workflows/macos-6.2.yml b/.github/workflows/macos-6.2.yml index 4ed23a99..b0fe6e9a 100644 --- a/.github/workflows/macos-6.2.yml +++ b/.github/workflows/macos-6.2.yml @@ -29,7 +29,7 @@ jobs: qt_arch: [clang_64] env: targetName: GoldenDict - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 05c67456..7624d090 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -30,7 +30,7 @@ jobs: qt_arch: [clang_64] env: targetName: GoldenDict - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index 8eb26c84..b0d38ee3 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -30,7 +30,7 @@ jobs: qt_ver: [6.2.4,6.3.0] qt_arch: [gcc_64] env: - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/ubuntu-PR-check.yml b/.github/workflows/ubuntu-PR-check.yml index 3ab651bb..5fad1eb0 100644 --- a/.github/workflows/ubuntu-PR-check.yml +++ b/.github/workflows/ubuntu-PR-check.yml @@ -24,7 +24,7 @@ jobs: qt_ver: [5.15.2,6.2.4] qt_arch: [gcc_64] env: - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 27a08629..09022a26 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -30,7 +30,7 @@ jobs: qt_ver: [5.15.2] qt_arch: [gcc_64] env: - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/windows-6.2.yml b/.github/workflows/windows-6.2.yml index 7e88dc86..8a227e06 100644 --- a/.github/workflows/windows-6.2.yml +++ b/.github/workflows/windows-6.2.yml @@ -31,7 +31,7 @@ jobs: qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/windows-PR-check.yml b/.github/workflows/windows-PR-check.yml index ff2258cd..ea7348bb 100644 --- a/.github/workflows/windows-PR-check.yml +++ b/.github/workflows/windows-PR-check.yml @@ -27,7 +27,7 @@ jobs: env: targetName: GoldenDict.exe - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 721b5ee1..c57f1628 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -32,7 +32,7 @@ jobs: qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe - version: 22.4.24 + version: 22.5.22 version-suffix: alpha prerelease: true # 步骤 diff --git a/goldendict.pro b/goldendict.pro index 5a32895c..a2c5133c 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = goldendict -VERSION = 22.4.24-alpha +VERSION = 22.5.22-alpha # Generate version file. We do this here and in a build rule described later. # The build rule is required since qmake isn't run each time the project is @@ -70,7 +70,7 @@ win32 { TARGET = GoldenDict win32-msvc* { - VERSION = 22.4.24 # VS does not recognize 22.number.alpha,cause errors during compilation under MSVC++ + VERSION = 22.5.22 # VS does not recognize 22.number.alpha,cause errors during compilation under MSVC++ DEFINES += __WIN32 _CRT_SECURE_NO_WARNINGS contains(QMAKE_TARGET.arch, x86_64) { DEFINES += NOMINMAX __WIN64 From fbfc2d7f438c2604d246f284369cdf6b3acadd64 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 21 May 2022 10:30:32 +0800 Subject: [PATCH 002/125] feature: add anki connect configuration --- config.cc | 31 ++++++++++++++++ config.hh | 11 ++++++ preferences.cc | 10 ++++++ preferences.ui | 95 +++++++++++++++++++++++++++++++++++--------------- 4 files changed, 119 insertions(+), 28 deletions(-) diff --git a/config.cc b/config.cc index 2e926248..d5f6d7fd 100644 --- a/config.cc +++ b/config.cc @@ -89,6 +89,10 @@ ProxyServer::ProxyServer(): enabled( false ), useSystemProxy( false ), type( Soc { } +AnkiConnectServer::AnkiConnectServer(): enabled( false ), host("127.0.0.1"), port( 8765 ) +{ +} + HotKey::HotKey(): modifiers( 0 ), key1( 0 ), key2( 0 ) { } @@ -937,6 +941,15 @@ Class load() c.preferences.proxyServer.systemProxyPassword = proxy.namedItem( "systemProxyPassword" ).toElement().text(); } + QDomNode ankiConnectServer = preferences.namedItem( "ankiConnectServer" ); + + if ( !ankiConnectServer.isNull() ) + { + c.preferences.ankiConnectServer.enabled = ( ankiConnectServer.toElement().attribute( "enabled" ) == "1" ); + c.preferences.ankiConnectServer.host = ankiConnectServer.namedItem( "host" ).toElement().text(); + c.preferences.ankiConnectServer.port = ankiConnectServer.namedItem( "port" ).toElement().text().toULong(); + } + if ( !preferences.namedItem( "checkForNewReleases" ).isNull() ) c.preferences.checkForNewReleases = ( preferences.namedItem( "checkForNewReleases" ).toElement().text() == "1" ); @@ -1872,6 +1885,24 @@ void save( Class const & c ) proxy.appendChild( opt ); } + //anki connect + { + QDomElement proxy = dd.createElement( "ankiConnectServer" ); + preferences.appendChild( proxy ); + + QDomAttr enabled = dd.createAttribute( "enabled" ); + enabled.setValue( c.preferences.ankiConnectServer.enabled ? "1" : "0" ); + proxy.setAttributeNode( enabled ); + + opt = dd.createElement( "host" ); + opt.appendChild( dd.createTextNode( c.preferences.ankiConnectServer.host ) ); + proxy.appendChild( opt ); + + opt = dd.createElement( "port" ); + opt.appendChild( dd.createTextNode( QString::number( c.preferences.ankiConnectServer.port ) ) ); + proxy.appendChild( opt ); + } + opt = dd.createElement( "checkForNewReleases" ); opt.appendChild( dd.createTextNode( c.preferences.checkForNewReleases ? "1" : "0" ) ); preferences.appendChild( opt ); diff --git a/config.hh b/config.hh index 774dbdef..de463bf7 100644 --- a/config.hh +++ b/config.hh @@ -137,6 +137,16 @@ struct ProxyServer ProxyServer(); }; +struct AnkiConnectServer +{ + bool enabled; + + QString host; + unsigned port; + + AnkiConnectServer(); +}; + // A hotkey -- currently qt modifiers plus one or two keys struct HotKey { @@ -329,6 +339,7 @@ struct Preferences QString audioPlaybackProgram; ProxyServer proxyServer; + AnkiConnectServer ankiConnectServer; bool checkForNewReleases; bool disallowContentFromOtherSites; diff --git a/preferences.cc b/preferences.cc index 6bafa8b7..6af7f8b6 100644 --- a/preferences.cc +++ b/preferences.cc @@ -323,6 +323,11 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ): ui.customSettingsGroup->setEnabled( p.proxyServer.enabled ); } + //anki connect + ui.useAnkiConnect->setChecked( p.ankiConnectServer.enabled ); + ui.ankiHost->setText( p.ankiConnectServer.host ); + ui.ankiPort->setValue( p.ankiConnectServer.port ); + connect( ui.customProxy, SIGNAL( toggled( bool ) ), this, SLOT( customProxyToggled( bool ) ) ); @@ -466,6 +471,11 @@ Config::Preferences Preferences::getPreferences() p.proxyServer.user = ui.proxyUser->text(); p.proxyServer.password = ui.proxyPassword->text(); + //anki connect + p.ankiConnectServer.enabled = ui.useAnkiConnect->isChecked(); + p.ankiConnectServer.host = ui.ankiHost->text(); + p.ankiConnectServer.port = (unsigned)ui.ankiPort->value(); + p.checkForNewReleases = ui.checkForNewReleases->isChecked(); p.disallowContentFromOtherSites = ui.disallowContentFromOtherSites->isChecked(); p.enableWebPlugins = ui.enableWebPlugins->isChecked(); diff --git a/preferences.ui b/preferences.ui index 8ce260f2..4aa7e8ff 100644 --- a/preferences.ui +++ b/preferences.ui @@ -24,7 +24,7 @@ - 0 + 4 @@ -993,6 +993,9 @@ for all program's network requests. + + false + Custom settings @@ -1011,6 +1014,9 @@ for all program's network requests. + + false + Host: @@ -1073,20 +1079,69 @@ for all program's network requests. - - - Qt::Vertical + + + true - - QSizePolicy::Fixed + + Anki Connect - - - 20 - 10 - + + true - + + false + + + + + + Host: + + + + + + + http:// + + + + + + + + + + Port: + + + + + + + 65535 + + + 8080 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -1176,22 +1231,6 @@ clears its network cache from disk during exit. - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - From 0ddf32f3eab42d89a0e8853211e8be3f501d0730 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Sat, 21 May 2022 14:21:41 +0800 Subject: [PATCH 003/125] Create how to connect with anki.md --- howto/how to connect with anki.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 howto/how to connect with anki.md diff --git a/howto/how to connect with anki.md b/howto/how to connect with anki.md new file mode 100644 index 00000000..915ac0df --- /dev/null +++ b/howto/how to connect with anki.md @@ -0,0 +1,23 @@ +# prerequisite +1. install anki +2. install ankiconnect + +# configure anki +## 1. the model must have `Front` and `Back` fields +![Snipaste_2022-05-21_14-08-21](https://user-images.githubusercontent.com/105986/169638410-c6aa8038-df03-40de-8731-9f0b9f43bf59.png) +## 2. configure the template +the front template + +![image](https://user-images.githubusercontent.com/105986/169638457-2358d020-0132-469f-a6b4-0fb6d1590fa2.png) +the back template + +![image](https://user-images.githubusercontent.com/105986/169638440-7191fcdd-c338-48a3-a899-7216a5c77425.png) + +# configure goldendict +## 1. through toolbar=>preference=>network +![image](https://user-images.githubusercontent.com/105986/169638498-fcb36ca7-02ac-4384-9a77-52aab657f5c1.png) + +## 2. action +![image](https://user-images.githubusercontent.com/105986/169638740-abecde84-d33b-45ce-932c-d465c6650334.png) +## 3. result +![image](https://user-images.githubusercontent.com/105986/169638761-f67c009d-27cd-440d-bafa-ebbdce9577e3.png) From 0a2661f9865b29720d7248dc1e04626a3cf08775 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 21 May 2022 14:03:26 +0800 Subject: [PATCH 004/125] add 'send to anki' function users can configure the ankiconnect to use together with anki --- ankiconnector.cpp | 85 ++++++++++++++++++++++++++++++ ankiconnector.h | 28 ++++++++++ articleview.cc | 22 ++++++++ articleview.hh | 4 ++ config.cc | 10 ++++ config.hh | 2 + goldendict.pro | 2 + locale/zh_CN.ts | 49 ++++++++++++++++- preferences.cc | 4 ++ preferences.ui | 131 ++++++++++++++++++++++++++++++---------------- utils.hh | 7 +++ 11 files changed, 297 insertions(+), 47 deletions(-) create mode 100644 ankiconnector.cpp create mode 100644 ankiconnector.h diff --git a/ankiconnector.cpp b/ankiconnector.cpp new file mode 100644 index 00000000..b7e11fc0 --- /dev/null +++ b/ankiconnector.cpp @@ -0,0 +1,85 @@ +#include "ankiconnector.h" +#include +#include +#include +#include "utils.hh" +AnkiConnector::AnkiConnector( QObject * parent, Config::Class const & _cfg ) : QObject{ parent }, cfg( _cfg ) +{ + mgr = new QNetworkAccessManager( this ); + connect( mgr, &QNetworkAccessManager::finished, this, &AnkiConnector::finishedSlot ); +} + +void AnkiConnector::sendToAnki( QString const & word, QString const & text ) +{ + //for simplicity. maybe use QJsonDocument in future? + QString postTemplate = QString( "{" + "\"action\": \"addNote\"," + "\"version\": 6," + "\"params\": {" + " \"note\": {" + " \"deckName\": \"%1\"," + " \"modelName\": \"%2\"," + " \"fields\":%3," + " \"options\": {" + " \"allowDuplicate\": true" + " }," + " \"tags\": []" + "}" + "}" + "}" + "" ); + + QJsonObject fields; + fields.insert( "Front", word ); + fields.insert( "Back", text ); + + QString postData = postTemplate.arg( cfg.preferences.ankiConnectServer.deck, + cfg.preferences.ankiConnectServer.model, + Utils::json2String( fields ) ); + +// qDebug().noquote() << postData; + QUrl url; + url.setScheme( "http" ); + url.setHost( cfg.preferences.ankiConnectServer.host ); + url.setPort( cfg.preferences.ankiConnectServer.port ); + QNetworkRequest request( url ); + request.setTransferTimeout( 3000 ); + // request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy ); + request.setHeader( QNetworkRequest::ContentTypeHeader, "applicaion/json" ); + auto reply = mgr->post( request, postData.toUtf8() ); + connect( reply, + &QNetworkReply::errorOccurred, + this, + [ this ]( QNetworkReply::NetworkError e ) + { + qWarning() << e; + emit this->errorText( tr( "anki: post to anki failed" ) ); + } ); +} + +void AnkiConnector::finishedSlot( QNetworkReply * reply ) +{ + if( reply->error() == QNetworkReply::NoError ) + { + QByteArray bytes = reply->readAll(); + QJsonDocument json = QJsonDocument::fromJson( bytes ); + auto obj = json.object(); + if( obj.size() != 2 || !obj.contains( "error" ) || !obj.contains( "result" ) || + obj[ "result" ].toString().isEmpty() ) + { + emit errorText( QObject::tr( "anki: post to anki failed" ) ); + } + QString result = obj[ "result" ].toString(); + + qDebug() << "anki result:" << result; + + emit errorText( tr( "anki: post to anki success" ) ); + } + else + { + qDebug() << "anki connect error" << reply->errorString(); + emit errorText( "anki:" + reply->errorString() ); + } + + reply->deleteLater(); +} diff --git a/ankiconnector.h b/ankiconnector.h new file mode 100644 index 00000000..e52a0f80 --- /dev/null +++ b/ankiconnector.h @@ -0,0 +1,28 @@ +#ifndef ANKICONNECTOR_H +#define ANKICONNECTOR_H + +#include "config.hh" + +#include +#include +#include + +class AnkiConnector : public QObject +{ + Q_OBJECT +public: + explicit AnkiConnector( QObject * parent, Config::Class const & cfg ); + + void sendToAnki( QString const & word, QString const & text ); + +private: + QNetworkAccessManager * mgr; + Config::Class const & cfg; +public : +signals: + void errorText( QString const & ); +private slots: + void finishedSlot(QNetworkReply * reply); +}; + +#endif // ANKICONNECTOR_H diff --git a/articleview.cc b/articleview.cc index d8382f98..88b1f71f 100644 --- a/articleview.cc +++ b/articleview.cc @@ -348,6 +348,11 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, Au channel = new QWebChannel(ui.definition->page()); agent = new ArticleViewAgent(this); attachWebChannelToHtml(); + ankiConnector = new AnkiConnector( this, cfg ); + connect( ankiConnector, + &AnkiConnector::errorText, + this, + [ this ]( QString const & errorText ) { emit statusBarMessage( errorText ); } ); } // explicitly report the minimum size, to avoid @@ -489,6 +494,10 @@ void ArticleView::showDefinition( QString const & word, QStringList const & dict ui.definition->setCursor( Qt::WaitCursor ); } +void ArticleView::sendToAnki(QString const & word, QString const & text ){ + ankiConnector->sendToAnki(word,text); +} + void ArticleView::showAnticipation() { ui.definition->setHtml( "" ); @@ -1721,6 +1730,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) QAction * followLinkExternal = 0; QAction * followLinkNewTab = 0; QAction * lookupSelection = 0; + QAction * sendToAnkiAction = 0 ; QAction * lookupSelectionGr = 0; QAction * lookupSelectionNewTab = 0; QAction * lookupSelectionNewTabGr = 0; @@ -1850,6 +1860,14 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) } } + // add anki menu + if( !text.isEmpty() && cfg.preferences.ankiConnectServer.enabled ) + { + QString txt = ui.definition->title(); + sendToAnkiAction = new QAction( tr( "&Send \"%1\" to anki with selected text." ).arg( txt ), &menu ); + menu.addAction( sendToAnkiAction ); + } + if( text.isEmpty() && !cfg.preferences.storeHistory) { QString txt = ui.definition->title(); @@ -1942,6 +1960,10 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) else if ( result == lookupSelection ) showDefinition( selectedText, getGroup( ui.definition->url() ), getCurrentArticle() ); + else if( result = sendToAnkiAction ) + { + sendToAnki( ui.definition->title(), ui.definition->selectedText() ); + } else if ( result == lookupSelectionGr && groupComboBox ) showDefinition( selectedText, groupComboBox->getCurrentGroup(), QString() ); diff --git a/articleview.hh b/articleview.hh index 921d640f..00d92a64 100644 --- a/articleview.hh +++ b/articleview.hh @@ -19,6 +19,7 @@ #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) #include #endif +#include "ankiconnector.h" class ResourceToSaveHandler; class ArticleViewAgent ; @@ -39,6 +40,8 @@ class ArticleView: public QFrame ArticleViewAgent * agent; Ui::ArticleView ui; + AnkiConnector * ankiConnector; + QAction pasteAction, articleUpAction, articleDownAction, goBackAction, goForwardAction, selectCurrentArticleAction, copyAsTextAction, inspectAction; @@ -129,6 +132,7 @@ public: QRegExp const & searchRegExp, unsigned group, bool ignoreDiacritics ); + void sendToAnki(QString const & word, QString const & text ); /// Clears the view and sets the application-global waiting cursor, /// which will be restored when some article loads eventually. void showAnticipation(); diff --git a/config.cc b/config.cc index d5f6d7fd..f4311f55 100644 --- a/config.cc +++ b/config.cc @@ -948,6 +948,8 @@ Class load() c.preferences.ankiConnectServer.enabled = ( ankiConnectServer.toElement().attribute( "enabled" ) == "1" ); c.preferences.ankiConnectServer.host = ankiConnectServer.namedItem( "host" ).toElement().text(); c.preferences.ankiConnectServer.port = ankiConnectServer.namedItem( "port" ).toElement().text().toULong(); + c.preferences.ankiConnectServer.deck = ankiConnectServer.namedItem( "deck" ).toElement().text(); + c.preferences.ankiConnectServer.model = ankiConnectServer.namedItem( "model" ).toElement().text(); } if ( !preferences.namedItem( "checkForNewReleases" ).isNull() ) @@ -1901,6 +1903,14 @@ void save( Class const & c ) opt = dd.createElement( "port" ); opt.appendChild( dd.createTextNode( QString::number( c.preferences.ankiConnectServer.port ) ) ); proxy.appendChild( opt ); + + opt = dd.createElement( "deck" ); + opt.appendChild( dd.createTextNode( c.preferences.ankiConnectServer.deck ) ); + proxy.appendChild( opt ); + + opt = dd.createElement( "model" ); + opt.appendChild( dd.createTextNode( c.preferences.ankiConnectServer.model ) ); + proxy.appendChild( opt ); } opt = dd.createElement( "checkForNewReleases" ); diff --git a/config.hh b/config.hh index de463bf7..9d1a0ae9 100644 --- a/config.hh +++ b/config.hh @@ -143,6 +143,8 @@ struct AnkiConnectServer QString host; unsigned port; + QString deck; + QString model; AnkiConnectServer(); }; diff --git a/goldendict.pro b/goldendict.pro index a2c5133c..40eba305 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -223,6 +223,7 @@ DEFINES += PROGRAM_VERSION=\\\"$$VERSION\\\" # Input HEADERS += folding.hh \ + ankiconnector.h \ article_inspect.h \ articlewebpage.h \ globalbroadcaster.h \ @@ -364,6 +365,7 @@ FORMS += groups.ui \ fulltextsearch.ui SOURCES += folding.cc \ + ankiconnector.cpp \ article_inspect.cpp \ articlewebpage.cpp \ globalbroadcaster.cpp \ diff --git a/locale/zh_CN.ts b/locale/zh_CN.ts index 9c9557ca..832cea28 100644 --- a/locale/zh_CN.ts +++ b/locale/zh_CN.ts @@ -39,6 +39,20 @@ (c) 2008-2013 Konstantin Isakov (ikm@goldendict.org) + + AnkiConnector + + + anki: post to anki failed + anki:发布成功 + anki:发布失败 + + + + anki: post to anki success + anki: 发布成功 + + ArticleInspector @@ -315,7 +329,12 @@ 引用的音频播放程序不存在。 - + + &Send "%1" to anki with selected text. + 将“%1”发送到anki并附带选择的文本。 + + + Sound files (*.wav *.ogg *.oga *.mp3 *.mp4 *.aac *.flac *.mid *.wv *.ape);;All files (*.*) 声音文件(*.wav *.ogg *.oga *.mp3 *.mp4 *.aac *.flac *.mid *.wv *.ape);;所有文件(*.*) @@ -3903,7 +3922,27 @@ however, the article from the topmost dictionary is shown. 自定义设置 - + + Anki Connect + Anki连接 + + + + http:// + http:// + + + + Deck: + 牌组: + + + + Model: + 模板: + + + Some sites detect GoldenDict via HTTP headers and block the requests. Enable this option to workaround the problem. 部分网站屏蔽了使用 GoldenDict 浏览器标识(UA)的请求,启用此选项以绕过该问题。 @@ -4420,6 +4459,12 @@ from Stardict, Babylon and GLS dictionaries Date: %1%2 日期:%1%2 + + + + anki: post to anki failed + anki:发布失败 + QuickFilterLine diff --git a/preferences.cc b/preferences.cc index 6af7f8b6..8a8e6872 100644 --- a/preferences.cc +++ b/preferences.cc @@ -327,6 +327,8 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ): ui.useAnkiConnect->setChecked( p.ankiConnectServer.enabled ); ui.ankiHost->setText( p.ankiConnectServer.host ); ui.ankiPort->setValue( p.ankiConnectServer.port ); + ui.ankiModel->setText( p.ankiConnectServer.model ); + ui.ankiDeck->setText(p.ankiConnectServer.deck); connect( ui.customProxy, SIGNAL( toggled( bool ) ), this, SLOT( customProxyToggled( bool ) ) ); @@ -475,6 +477,8 @@ Config::Preferences Preferences::getPreferences() p.ankiConnectServer.enabled = ui.useAnkiConnect->isChecked(); p.ankiConnectServer.host = ui.ankiHost->text(); p.ankiConnectServer.port = (unsigned)ui.ankiPort->value(); + p.ankiConnectServer.deck = ui.ankiDeck->text(); + p.ankiConnectServer.model = ui.ankiModel->text(); p.checkForNewReleases = ui.checkForNewReleases->isChecked(); p.disallowContentFromOtherSites = ui.disallowContentFromOtherSites->isChecked(); diff --git a/preferences.ui b/preferences.ui index 4aa7e8ff..f4df1e9f 100644 --- a/preferences.ui +++ b/preferences.ui @@ -24,7 +24,7 @@ - 4 + 0 @@ -1092,53 +1092,94 @@ for all program's network requests. false - + - - - Host: - - + + + + + Host: + + + + + + + http:// + + + + + + + + + + Port: + + + + + + + 65535 + + + 8080 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - http:// - - - - - - - - - - Port: - - - - - - - 65535 - - - 8080 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - + + + + + Deck: + + + + + + + + + + Model: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + diff --git a/utils.hh b/utils.hh index 2021223c..36318167 100644 --- a/utils.hh +++ b/utils.hh @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace Utils { @@ -77,6 +79,11 @@ inline bool ignoreKeyEvent(QKeyEvent *keyEvent) { return false; } +inline QString json2String( const QJsonObject & json ) +{ + return QString( QJsonDocument( json ).toJson( QJsonDocument::Compact ) ); +} + namespace AtomicInt { From 40f67d7f0f2f3695536ad8fbc04f7e10a9122ef7 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 21 May 2022 22:50:04 +0800 Subject: [PATCH 005/125] fix: dsl dictionary crash when skip expandOptionalParts as headword is too long --- dsl.cc | 7 +++---- dsl_details.cc | 6 ------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/dsl.cc b/dsl.cc index 6991e053..6302feee 100644 --- a/dsl.cc +++ b/dsl.cc @@ -2153,10 +2153,9 @@ vector< sptr< Dictionary::Class > > makeDictionaries( hasString = false; - // The line read should either consist of pure whitespace, or be a - // headword - - if ( curString.empty() ) + // The line read should either consist of pure whitespace, or be a headword + // skip too long headword,it can never be headword. + if( curString.empty() || curString.size() > 100 ) continue; if ( isDslWs( curString[ 0 ] ) ) diff --git a/dsl_details.cc b/dsl_details.cc index c317b445..5accaf57 100644 --- a/dsl_details.cc +++ b/dsl_details.cc @@ -1155,12 +1155,6 @@ void expandOptionalParts( wstring & str, list< wstring > * result, list< wstring > * headwords; headwords = inside_recurse ? result : &expanded; - //if str is too long ,it can never be headwords. - //todo? - if( str.size() > 100 ) - { - return; - } for( ; x < str.size(); ) { wchar ch = str[ x ]; From dcaaae4dc87f2c536b40025fc73033a1363d59d2 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Sat, 21 May 2022 23:10:30 +0800 Subject: [PATCH 006/125] Update how to connect with anki.md --- howto/how to connect with anki.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/howto/how to connect with anki.md b/howto/how to connect with anki.md index 915ac0df..d5eeae08 100644 --- a/howto/how to connect with anki.md +++ b/howto/how to connect with anki.md @@ -15,7 +15,7 @@ the back template # configure goldendict ## 1. through toolbar=>preference=>network -![image](https://user-images.githubusercontent.com/105986/169638498-fcb36ca7-02ac-4384-9a77-52aab657f5c1.png) +![image](https://user-images.githubusercontent.com/105986/169657672-d1affbde-e80e-4110-8fd9-55f2645c5ee1.png) ## 2. action ![image](https://user-images.githubusercontent.com/105986/169638740-abecde84-d33b-45ce-932c-d465c6650334.png) From 55fee75acdbb45e63d000b1e4ab1f10f933b03c7 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 22 May 2022 10:05:22 +0800 Subject: [PATCH 007/125] fix: a bug with send to anki --- articleview.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articleview.cc b/articleview.cc index 88b1f71f..23940893 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1960,7 +1960,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) else if ( result == lookupSelection ) showDefinition( selectedText, getGroup( ui.definition->url() ), getCurrentArticle() ); - else if( result = sendToAnkiAction ) + else if( result == sendToAnkiAction ) { sendToAnki( ui.definition->title(), ui.definition->selectedText() ); } From 435cadfef8989bcc52e4291a203d7cdf4008645b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 22 May 2022 16:25:03 +0800 Subject: [PATCH 008/125] pro file update c++17 support --- goldendict.pro | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/goldendict.pro b/goldendict.pro index 40eba305..9b0906fa 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -42,11 +42,10 @@ DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050F00 DEFINES += MAKE_FFMPEG_PLAYER } -#QT += sql CONFIG += exceptions \ rtti \ stl \ - c++14 \ + c++17 \ lrelease \ embed_translations @@ -76,13 +75,12 @@ win32 { DEFINES += NOMINMAX __WIN64 } LIBS += -L$${PWD}/winlibs/lib/msvc - QMAKE_CXXFLAGS += /wd4290 # silence the warning C4290: C++ exception specification ignored + QMAKE_CXXFLAGS += /wd4290 /Zc:__cplusplus /std:c++17 # silence the warning C4290: C++ exception specification ignored # QMAKE_LFLAGS_RELEASE += /OPT:REF /OPT:ICF # QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG CONFIG+=force_debug_info QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO - DEFINES += GD_NO_MANIFEST # QMAKE_CXXFLAGS_RELEASE += /GL # slows down the linking significantly LIBS += -lshell32 -luser32 -lsapi -lole32 Debug: LIBS+= -lhunspelld From 467b2d17b3d3fb0635022fc00a09216a9efca58e Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 00:13:24 +0800 Subject: [PATCH 009/125] fix: duplicated entries in epwing dictionary --- epwing_book.cc | 42 +++++++++++++++++++++++++++++++++--------- epwing_book.hh | 2 +- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/epwing_book.cc b/epwing_book.cc index bec5995d..111f031a 100644 --- a/epwing_book.cc +++ b/epwing_book.cc @@ -850,7 +850,7 @@ void EpwingBook::getFirstHeadword( EpwingHeadword & head ) fixHeadword( head.headword ); EWPos epos( pos.page, pos.offset ); - allHeadwordPositions[ head.headword ] = epos; + allHeadwordPositions[ head.headword ] << epos; } bool EpwingBook::getNextHeadword( EpwingHeadword & head ) @@ -881,13 +881,25 @@ bool EpwingBook::getNextHeadword( EpwingHeadword & head ) if( allHeadwordPositions.contains( head.headword ) ) { - EWPos epos = allHeadwordPositions[ head.headword ]; - if( pos.page != epos.first || abs( pos.offset - epos.second ) > 4 ) + // existed position + bool existed = false; + foreach( EWPos epos, allHeadwordPositions[ head.headword ] ) + { + if( pos.page == epos.first && abs( pos.offset - epos.second ) <= 4 ) + { + existed = true; + break; + } + } + if( !existed ) + { + allHeadwordPositions[ head.headword ] << EWPos( pos.page, pos.offset ); return true; + } } else { - allHeadwordPositions[ head.headword ] = EWPos( pos.page, pos.offset ); + allHeadwordPositions[ head.headword ]< 4 ) - break; + // existed position + bool existed = false; + foreach( EWPos epos, allHeadwordPositions[ head.headword ] ) + { + if( pos.page == epos.first && abs( pos.offset - epos.second ) <= 4 ) + { + existed = true; + break; + } + } + if( !existed ) + { + allHeadwordPositions[ head.headword ] << EWPos( pos.page, pos.offset ); + return true; + } } else { - allHeadwordPositions[ head.headword ] = EWPos( pos.page, pos.offset ); - break; + allHeadwordPositions[ head.headword ]< baseFontsMap, customFontsMap; QVector< int > refPages, refOffsets; - QMap< QString, EWPos > allHeadwordPositions; + QMap< QString, QList > allHeadwordPositions; QVector< EWPos > LinksQueue; int refOpenCount, refCloseCount; static Mutex libMutex; From b41bef9a314c73458eaded8ae3517acf9e7fa1eb Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 00:26:12 +0800 Subject: [PATCH 010/125] fix: skip too long headword in epwing dictionary --- epwing.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/epwing.cc b/epwing.cc index cd5eec5c..9d67cd2d 100644 --- a/epwing.cc +++ b/epwing.cc @@ -1054,7 +1054,8 @@ vector< sptr< Dictionary::Class > > makeDictionaries( for( ; ; ) { - if( !head.headword.isEmpty() ) + //skip too long headword + if( !head.headword.isEmpty() && head.headword.size() < 30 ) { uint32_t offset = chunks.startNewBlock(); chunks.addToBlock( &head.page, sizeof( head.page ) ); From fe1fd6b9337145bdb9fb64318fd7acec05cfb207 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 20:14:26 +0800 Subject: [PATCH 011/125] fix: webengineview font lose affect after restart the application --- mainwindow.cc | 19 +++++++++++++++++++ mainwindow.hh | 2 ++ 2 files changed, 21 insertions(+) diff --git a/mainwindow.cc b/mainwindow.cc index 55db62e1..28ad0dc8 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -60,6 +60,9 @@ #include "wstring_qt.hh" #endif +#include +#include + #ifdef HAVE_X11 #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) #include @@ -90,6 +93,19 @@ class InitSSLRunnable : public QRunnable #endif +void MainWindow::changeWebEngineViewFont() +{ + if( cfg.preferences.webFontFamily.isEmpty() ) + { + QWebEngineProfile::defaultProfile()->settings()->resetFontFamily( QWebEngineSettings::StandardFont ); + } + else + { + QWebEngineProfile::defaultProfile()->settings()->setFontFamily( QWebEngineSettings::StandardFont, + cfg.preferences.webFontFamily ); + } +} + MainWindow::MainWindow( Config::Class & cfg_ ): trayIcon( 0 ), groupLabel( &searchPaneTitleBar ), @@ -761,6 +777,9 @@ MainWindow::MainWindow( Config::Class & cfg_ ): applyProxySettings(); + //set webengineview font + changeWebEngineViewFont(); + connect( &dictNetMgr, SIGNAL( proxyAuthenticationRequired( QNetworkProxy, QAuthenticator * ) ), this, SLOT( proxyAuthentication( QNetworkProxy, QAuthenticator * ) ) ); diff --git a/mainwindow.hh b/mainwindow.hh index 7260ce3c..4e3e4831 100644 --- a/mainwindow.hh +++ b/mainwindow.hh @@ -260,6 +260,8 @@ private: TranslateBoxPopup popupAction ); void setTranslateBoxTextAndClearSuffix( QString const & text, WildcardPolicy wildcardPolicy, TranslateBoxPopup popupAction ); + void changeWebEngineViewFont(); + private slots: void hotKeyActivated( int ); From 7b434cffaf5faee619520ccc803c6b8c2c33fa25 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 20:42:37 +0800 Subject: [PATCH 012/125] fix:epwing code format --- epwing_book.cc | 4 ++-- epwing_book.hh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/epwing_book.cc b/epwing_book.cc index 111f031a..7f8b2704 100644 --- a/epwing_book.cc +++ b/epwing_book.cc @@ -899,7 +899,7 @@ bool EpwingBook::getNextHeadword( EpwingHeadword & head ) } else { - allHeadwordPositions[ head.headword ]< baseFontsMap, customFontsMap; QVector< int > refPages, refOffsets; - QMap< QString, QList > allHeadwordPositions; + QMap< QString, QList< EWPos > > allHeadwordPositions; QVector< EWPos > LinksQueue; int refOpenCount, refCloseCount; static Mutex libMutex; From 9f5788ee1180439730c4b7f5f78ac7c919cd9741 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Mon, 23 May 2022 12:06:28 +0300 Subject: [PATCH 013/125] Remove unused declaration showFindButtons() c2cdf9b177b4995fa069820a512668befb3135fa declared this member function but it was never defined. --- articleview.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/articleview.hh b/articleview.hh index 2c274e7e..8a38d00d 100644 --- a/articleview.hh +++ b/articleview.hh @@ -62,7 +62,6 @@ class ArticleView: public QFrame void highlightFTSResults(); void performFtsFindOperation( bool backwards ); - void showFindButtons(); public: /// The popupView flag influences contents of the context menus to be From bd5b36cac79637b849b4bc665a54e99b9b0706a2 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sun, 22 May 2022 22:12:30 +0300 Subject: [PATCH 014/125] Extract ArticleView::highlightAllFtsOccurences() Extracting this function allows to simplify the code and facilitates optimizing it in the next commit. Remove `#if QT_VERSION >= 0x040600` along the way as GoldenDict does not support Qt versions older than 4.6 for several years now. --- articleview.cc | 17 ++++++++--------- articleview.hh | 1 + 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/articleview.cc b/articleview.cc index ac2bdf6e..2e01b7f0 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2643,17 +2643,9 @@ void ArticleView::highlightFTSResults() if( ftsSearchMatchCase ) flags |= QWebPage::FindCaseSensitively; -#if QT_VERSION >= 0x040600 - flags |= QWebPage::HighlightAllOccurrences; - - for( int x = 0; x < allMatches.size(); x++ ) - ui.definition->findText( allMatches.at( x ), flags ); - - flags &= ~QWebPage::HighlightAllOccurrences; -#endif - if( !allMatches.isEmpty() ) { + highlightAllFtsOccurences( flags ); if( ui.definition->findText( allMatches.at( 0 ), flags ) ) { ui.definition->page()->currentFrame()-> @@ -2669,6 +2661,13 @@ void ArticleView::highlightFTSResults() ftsSearchIsOpened = true; } +void ArticleView::highlightAllFtsOccurences( QWebPage::FindFlags flags ) +{ + flags |= QWebPage::HighlightAllOccurrences; + for( int x = 0; x < allMatches.size(); x++ ) + ui.definition->findText( allMatches.at( x ), flags ); +} + void ArticleView::performFtsFindOperation( bool backwards ) { if( !ftsSearchIsOpened ) diff --git a/articleview.hh b/articleview.hh index 8a38d00d..ebc9dbaf 100644 --- a/articleview.hh +++ b/articleview.hh @@ -61,6 +61,7 @@ class ArticleView: public QFrame int ftsPosition; void highlightFTSResults(); + void highlightAllFtsOccurences( QWebPage::FindFlags flags ); void performFtsFindOperation( bool backwards ); public: From b87b023db0a7fcd2fdc92916dd572afe8926040c Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sun, 22 May 2022 22:21:00 +0300 Subject: [PATCH 015/125] Optimize highlighting FTS matches in articles The wall time of calls to ArticleView::highlightAllFtsOccurences() on my GNU/Linux system before and at this commit: allMatches.size() uniqueMatches.size() before(ms) at(ms) 79 1 277 4 98 1 380 4 267 1 16803 65 --- articleview.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/articleview.cc b/articleview.cc index 2e01b7f0..8de8efff 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2664,8 +2664,22 @@ void ArticleView::highlightFTSResults() void ArticleView::highlightAllFtsOccurences( QWebPage::FindFlags flags ) { flags |= QWebPage::HighlightAllOccurrences; - for( int x = 0; x < allMatches.size(); x++ ) - ui.definition->findText( allMatches.at( x ), flags ); + + // Usually allMatches contains mostly duplicates. Thus searching for each element of + // allMatches to highlight them takes a long time => collect unique elements into a + // set and search for them instead. + // Don't use QList::toSet() or QSet's range constructor because they reserve space + // for QList::size() elements, whereas the final QSet size is likely 1 or 2. + QSet< QString > uniqueMatches; + for( int x = 0; x < allMatches.size(); ++x ) + { + QString const & match = allMatches.at( x ); + // Consider words that differ only in case equal if the search is case-insensitive. + uniqueMatches.insert( ftsSearchMatchCase ? match : match.toLower() ); + } + + for( QSet< QString >::const_iterator it = uniqueMatches.constBegin(); it != uniqueMatches.constEnd(); ++it ) + ui.definition->findText( *it, flags ); } void ArticleView::performFtsFindOperation( bool backwards ) From de11e573d3bcc1d5cbb5d31bc21431a9b6c27016 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 23:38:04 +0800 Subject: [PATCH 016/125] fix:quit application failed when the inspector windows is show --- mainwindow.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mainwindow.cc b/mainwindow.cc index 28ad0dc8..8f5df588 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -1244,6 +1244,10 @@ void MainWindow::closeEvent( QCloseEvent * ev ) void MainWindow::quitApp() { + if( inspector && inspector->isVisible() ) + { + inspector->hide(); + } commitData(); qApp->quit(); } From bb9811bdf454d13f7bee9d98a76cbebee5f22afb Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 23:42:52 +0800 Subject: [PATCH 017/125] clean code: remove unused line --- article_netmgr.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index e63ebfff..db2aa0e9 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -131,22 +131,11 @@ using std::string; emit errorOccurred( code ); } -// void AllowFrameReply::readDataFromBase() -// { -//// QByteArray data; -//// data.resize( baseReply->bytesAvailable() ); -//// baseReply->read( data.data(), data.size() ); -//// buffer += data; -// emit readyRead(); -// } - qint64 AllowFrameReply::readData( char * data, qint64 maxSize ) { auto bytesAvailable= baseReply->bytesAvailable(); qint64 size = qMin( maxSize, bytesAvailable ); baseReply->read( data, size ); -// memcpy( data, buffer.data(), size ); -// buffer.remove( 0, size ); return size; } From f07ba75a5cdf6b1307491035d684bc13d31b437f Mon Sep 17 00:00:00 2001 From: Abs62 Date: Mon, 23 May 2022 20:43:58 +0300 Subject: [PATCH 018/125] Epwing: Fix full-text search for some CJK dictionaries (issue #1490) --- epwing_book.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/epwing_book.cc b/epwing_book.cc index 28d8eadc..50e6b4b3 100644 --- a/epwing_book.cc +++ b/epwing_book.cc @@ -1099,6 +1099,9 @@ void EpwingBook::getArticle( QString & headword, QString & articleText, headword = QString::fromUtf8( buffer, length ); finalizeText( headword ); + if( text_only ) + fixHeadword( headword ); + articleText = getText( pos.page, pos.offset, text_only ); } From 19a09b5f1b3c12cf5f34f02651b232140d50ff6b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 24 May 2022 21:03:25 +0800 Subject: [PATCH 019/125] fix:remove epwing headword size limitation --- epwing.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/epwing.cc b/epwing.cc index 9d67cd2d..cd5eec5c 100644 --- a/epwing.cc +++ b/epwing.cc @@ -1054,8 +1054,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( for( ; ; ) { - //skip too long headword - if( !head.headword.isEmpty() && head.headword.size() < 30 ) + if( !head.headword.isEmpty() ) { uint32_t offset = chunks.startNewBlock(); chunks.addToBlock( &head.page, sizeof( head.page ) ); From b55da16c332da5873612ab94571178f5cb7c571b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 24 May 2022 21:25:21 +0800 Subject: [PATCH 020/125] clean code: remove deprecated css properties --- article-style.css | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/article-style.css b/article-style.css index 16ce7887..867bb873 100644 --- a/article-style.css +++ b/article-style.css @@ -914,7 +914,6 @@ div.xdxf left: 0px; width: 0px; background-color: white; - background-color: Window; border-style: solid; border-color: #AAAAAA; border-width: 1px; @@ -934,9 +933,7 @@ div.xdxf .mwiki td.os-suggest-result, .mwiki td.os-suggest-result-hl { white-space: nowrap; background-color: white; - background-color: Window; color: black; - color: WindowText; padding: 2px; } .mwiki td.os-suggest-result-hl, @@ -947,8 +944,8 @@ div.xdxf .mwiki td.os-suggest-result-hl { /* System colors are misimplemented in Safari 3.0 and earlier, making highlighted text illegible... */ - background-color: Highlight; - color: HighlightText; + /* background-color: Highlight; + color: HighlightText; */ } .mwiki .os-suggest-toggle { From 1f01e545d188c6298426b26b87588951e881b855 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 24 May 2022 21:40:53 +0800 Subject: [PATCH 021/125] disable print background --- articleview.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/articleview.cc b/articleview.cc index b70e2077..b3543845 100644 --- a/articleview.cc +++ b/articleview.cc @@ -325,6 +325,7 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, Au settings->defaultSettings()->setAttribute( QWebEngineSettings::PluginsEnabled, cfg.preferences.enableWebPlugins ); settings->defaultSettings()->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false ); settings->defaultSettings()->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true ); + settings->defaultSettings()->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false ); #else settings->setAttribute( QWebEngineSettings::LocalContentCanAccessRemoteUrls, true ); settings->setAttribute( QWebEngineSettings::LocalContentCanAccessFileUrls, true ); @@ -332,6 +333,7 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, Au settings->setAttribute( QWebEngineSettings::PluginsEnabled, cfg.preferences.enableWebPlugins ); settings->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false ); settings->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true ); + settings->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false ); #endif expandOptionalParts = cfg.preferences.alwaysExpandOptionalParts; From efb4a84da2248ef370ce52126724f6003c10524b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 23 May 2022 00:13:24 +0800 Subject: [PATCH 022/125] fix: duplicated entries in epwing dictionary --- epwing.cc | 2 +- epwing_book.cc | 42 +++++++++++++++++++++++++++++++++--------- epwing_book.hh | 2 +- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/epwing.cc b/epwing.cc index 76866cb9..dfc14949 100644 --- a/epwing.cc +++ b/epwing.cc @@ -44,7 +44,7 @@ namespace { enum { Signature = 0x58575045, // EPWX on little-endian, XWPE on big-endian - CurrentFormatVersion = 5 + BtreeIndexing::FormatVersion + Folding::Version + CurrentFormatVersion = 6 + BtreeIndexing::FormatVersion + Folding::Version }; struct IdxHeader diff --git a/epwing_book.cc b/epwing_book.cc index 50e6b4b3..1636f1af 100644 --- a/epwing_book.cc +++ b/epwing_book.cc @@ -846,7 +846,7 @@ void EpwingBook::getFirstHeadword( EpwingHeadword & head ) fixHeadword( head.headword ); EWPos epos( pos.page, pos.offset ); - allHeadwordPositions[ head.headword ] = epos; + allHeadwordPositions[ head.headword ] << epos; } bool EpwingBook::getNextHeadword( EpwingHeadword & head ) @@ -877,13 +877,25 @@ bool EpwingBook::getNextHeadword( EpwingHeadword & head ) if( allHeadwordPositions.contains( head.headword ) ) { - EWPos epos = allHeadwordPositions[ head.headword ]; - if( pos.page != epos.first || abs( pos.offset - epos.second ) > 4 ) + // existed position + bool existed = false; + foreach( EWPos epos, allHeadwordPositions[ head.headword ] ) + { + if( pos.page == epos.first && abs( pos.offset - epos.second ) <= 4 ) + { + existed = true; + break; + } + } + if( !existed ) + { + allHeadwordPositions[ head.headword ] << EWPos( pos.page, pos.offset ); return true; + } } else { - allHeadwordPositions[ head.headword ] = EWPos( pos.page, pos.offset ); + allHeadwordPositions[ head.headword ] << EWPos( pos.page, pos.offset ); return true; } } @@ -939,14 +951,26 @@ bool EpwingBook::getNextHeadword( EpwingHeadword & head ) if( allHeadwordPositions.contains( head.headword ) ) { - EWPos epos = allHeadwordPositions[ head.headword ]; - if( pos.page != epos.first || abs( pos.offset - epos.second ) > 4 ) - break; + // existed position + bool existed = false; + foreach( EWPos epos, allHeadwordPositions[ head.headword ] ) + { + if( pos.page == epos.first && abs( pos.offset - epos.second ) <= 4 ) + { + existed = true; + break; + } + } + if( !existed ) + { + allHeadwordPositions[ head.headword ] << EWPos( pos.page, pos.offset ); + return true; + } } else { - allHeadwordPositions[ head.headword ] = EWPos( pos.page, pos.offset ); - break; + allHeadwordPositions[ head.headword ] << EWPos( pos.page, pos.offset ); + return true; } } diff --git a/epwing_book.hh b/epwing_book.hh index d7be2d3c..da558024 100644 --- a/epwing_book.hh +++ b/epwing_book.hh @@ -74,7 +74,7 @@ class EpwingBook QStringList imageCacheList, soundsCacheList, moviesCacheList, fontsCacheList; QMap< QString, QString > baseFontsMap, customFontsMap; QVector< int > refPages, refOffsets; - QMap< QString, EWPos > allHeadwordPositions; + QMap< QString, QList< EWPos > > allHeadwordPositions; QVector< EWPos > LinksQueue; int refOpenCount, refCloseCount; static Mutex libMutex; From 201f11e65638365e7ef996bf43386581ff4bd075 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Tue, 24 May 2022 14:47:15 +0300 Subject: [PATCH 023/125] Set current article at most once in loadFinished() When the current article is set and the user expands or collapses optional parts (e.g. via the Ctrl+* shortcut), ArticleView::setCurrentArticle() is called twice from ArticleView::loadFinished(). Furthermore, the window scroll position is restored before the second jump. This is wasteful. Move the higher-priority setCurrentArticle() call up and, if it succeeds, skip the other call and the scrolling. I have measured the time spent running the affected code fragment on my GNU/Linux system before and at this commit. When the loaded articles are not very large, the performance gain of this commit is only about 1 ms. However, when one of the displayed articles was huge (the "United States" English Wikipedia article), the time went from 120 ms to 5 ms. --- articleview.cc | 102 +++++++++++++++++++++++++------------------------ articleview.hh | 3 +- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/articleview.cc b/articleview.cc index 8de8efff..781e700e 100644 --- a/articleview.cc +++ b/articleview.cc @@ -544,60 +544,63 @@ void ArticleView::loadFinished( bool ) qApp->sendEvent( ui.definition, &ev ); } - QVariant userDataVariant = ui.definition->history()->currentItem().userData(); - - if ( userDataVariant.type() == QVariant::Map ) - { - QMap< QString, QVariant > userData = userDataVariant.toMap(); - - QString currentArticle = userData.value( "currentArticle" ).toString(); - - if ( currentArticle.size() ) - { - // There's an active article saved, so set it to be active. - setCurrentArticle( currentArticle ); - } - - double sx = 0, sy = 0; - - if ( userData.value( "sx" ).type() == QVariant::Double ) - sx = userData.value( "sx" ).toDouble(); - - if ( userData.value( "sy" ).type() == QVariant::Double ) - sy = userData.value( "sy" ).toDouble(); - - if ( sx != 0 || sy != 0 ) - { - // Restore scroll position - ui.definition->page()->mainFrame()->evaluateJavaScript( - QString( "window.scroll( %1, %2 );" ).arg( sx ).arg( sy ) ); - } - } - else - { - QString const scrollTo = Qt4x5::Url::queryItemValue( url, "scrollto" ); - if( isScrollTo( scrollTo ) ) - { - // There is no active article saved in history, but we have it as a parameter. - // setCurrentArticle will save it and scroll there. - setCurrentArticle( scrollTo, true ); - } - } - - - ui.definition->unsetCursor(); - //QApplication::restoreOverrideCursor(); - // Expand collapsed article if only one loaded - ui.definition->page()->mainFrame()->evaluateJavaScript( QString( "gdCheckArticlesNumber();" ) ); + ui.definition->page()->mainFrame()->evaluateJavaScript( "gdCheckArticlesNumber();" ); + bool jumpedToCurrentArticle = false; // Jump to current article after page reloading if( !articleToJump.isEmpty() ) { - setCurrentArticle( articleToJump, true ); + jumpedToCurrentArticle = setCurrentArticle( articleToJump, true ); articleToJump.clear(); } + if( !jumpedToCurrentArticle ) + { + QVariant userDataVariant = ui.definition->history()->currentItem().userData(); + + if ( userDataVariant.type() == QVariant::Map ) + { + QMap< QString, QVariant > userData = userDataVariant.toMap(); + + QString currentArticle = userData.value( "currentArticle" ).toString(); + + if ( currentArticle.size() ) + { + // There's an active article saved, so set it to be active. + setCurrentArticle( currentArticle ); + } + + double sx = 0, sy = 0; + + if ( userData.value( "sx" ).type() == QVariant::Double ) + sx = userData.value( "sx" ).toDouble(); + + if ( userData.value( "sy" ).type() == QVariant::Double ) + sy = userData.value( "sy" ).toDouble(); + + if ( sx != 0 || sy != 0 ) + { + // Restore scroll position + ui.definition->page()->mainFrame()->evaluateJavaScript( + QString( "window.scroll( %1, %2 );" ).arg( sx ).arg( sy ) ); + } + } + else + { + QString const scrollTo = Qt4x5::Url::queryItemValue( url, "scrollto" ); + if( isScrollTo( scrollTo ) ) + { + // There is no active article saved in history, but we have it as a parameter. + // setCurrentArticle will save it and scroll there. + setCurrentArticle( scrollTo, true ); + } + } + } + + ui.definition->unsetCursor(); + //QApplication::restoreOverrideCursor(); + #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) if( !Qt4x5::Url::queryItemValue( url, "gdanchor" ).isEmpty() ) { @@ -729,13 +732,13 @@ void ArticleView::jumpToDictionary( QString const & id, bool force ) } } -void ArticleView::setCurrentArticle( QString const & id, bool moveToIt ) +bool ArticleView::setCurrentArticle( QString const & id, bool moveToIt ) { if ( !isScrollTo( id ) ) - return; // Incorrect id + return false; // Incorrect id if ( !ui.definition->isVisible() ) - return; // No action on background page, scrollIntoView there don't work + return false; // No action on background page, scrollIntoView there don't work QString const dictionaryId = dictionaryIdFromScrollTo( id ); if ( getArticlesList().contains( dictionaryId ) ) @@ -751,6 +754,7 @@ void ArticleView::setCurrentArticle( QString const & id, bool moveToIt ) ui.definition->page()->mainFrame()->evaluateJavaScript( QString( "gdMakeArticleActive( '%1' );" ).arg( dictionaryId ) ); } + return true; } void ArticleView::selectCurrentArticle() diff --git a/articleview.hh b/articleview.hh index ebc9dbaf..8512d821 100644 --- a/articleview.hh +++ b/articleview.hh @@ -319,7 +319,8 @@ private: /// Sets the current article by executing a javascript code. /// If moveToIt is true, it moves the focus to it as well. - void setCurrentArticle( QString const &, bool moveToIt = false ); + /// Returns true in case of success, false otherwise. + bool setCurrentArticle( QString const &, bool moveToIt = false ); /// Checks if the given article in form of "gdfrom-xxx" is inside a "website" /// frame. From 12f7db741438434d0516fc28ed0988482359be99 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 25 May 2022 19:45:19 +0800 Subject: [PATCH 024/125] fix: remove deprecated css --- article-style.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/article-style.css b/article-style.css index 867bb873..903981fd 100644 --- a/article-style.css +++ b/article-style.css @@ -941,12 +941,6 @@ div.xdxf background-color: #4C59A6; color: white; } -.mwiki td.os-suggest-result-hl { - /* System colors are misimplemented in Safari 3.0 and earlier, - making highlighted text illegible... */ - /* background-color: Highlight; - color: HighlightText; */ -} .mwiki .os-suggest-toggle { position: relative; From c770e9688e7369762de8be27034bfd10958f4cb6 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Wed, 25 May 2022 12:30:57 +0300 Subject: [PATCH 025/125] Don't update state prematurely in showTranslationFor() MainWindow::showTranslationFor() overloads disable the "Pronounce Word" action, then call ArticleView::showDefinition(). And then immediately update pronounce availability, Found in Dictionaries list, Back and Forward buttons. Since ArticleView::showDefinition() loads the requested page asynchronously, the previous page is still current. Therefore the "Pronounce Word" action is immediately re-enabled (if the still-current article has sounds), the other state updates have no effect whatsoever. Once the new page is loaded, the state is updated again in MainWindow::pageLoaded() - this time with the desired effect. So the only effect of the state updates in MainWindow::showTranslationFor() is to revert the intentional disabling of the "Pronounce Word" action. Plus waste some CPU time. The pronunciation-disabling behavior looks better to me and is consistent with the scan popup's behavior (which immediately hides the "Pronounce Word" button). --- mainwindow.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/mainwindow.cc b/mainwindow.cc index 73f29882..4be02c6a 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -2806,11 +2806,6 @@ void MainWindow::showTranslationFor( Config::InputPhrase const & phrase, view->showDefinition( phrase, group, scrollTo ); - updatePronounceAvailability(); - updateFoundInDictsList(); - - updateBackForwardButtons(); - #if 0 QUrl req; @@ -2917,11 +2912,6 @@ void MainWindow::showTranslationFor( QString const & inWord, view->showDefinition( inWord, dictIDs, searchRegExp, groupInstances[ groupList->currentIndex() ].id, ignoreDiacritics ); - - updatePronounceAvailability(); - updateFoundInDictsList(); - - updateBackForwardButtons(); } #ifdef HAVE_X11 From 0cea62699d3aeee77d9c363028303ba6e591ae2c Mon Sep 17 00:00:00 2001 From: wisherhxl Date: Mon, 23 May 2022 11:27:57 +0800 Subject: [PATCH 026/125] Linux-specific: workaround of clipboard issue with Ctrl+C+C Closes #1420, closes #650, closes #858. --- mainwindow.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mainwindow.cc b/mainwindow.cc index 4be02c6a..f06508cd 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -3095,7 +3095,19 @@ void MainWindow::hotKeyActivated( int hk ) toggleMainWindow(); else if ( scanPopup.get() ) + { +#ifdef HAVE_X11 + // When the user requests translation with the Ctrl+C+C hotkey in certain apps + // on some GNU/Linux systems, GoldenDict appears to handle Ctrl+C+C before the + // active application finishes handling Ctrl+C. As a result, GoldenDict finds + // the clipboard empty, silently cancels the translation request, and users report + // that Ctrl+C+C is broken in these apps. Slightly delay handling the clipboard + // hotkey to give the active application more time and thus work around the issue. + QTimer::singleShot( 10, scanPopup.get(), SLOT( translateWordFromClipboard() ) ); +#else scanPopup->translateWordFromClipboard(); +#endif + } } void MainWindow::prepareNewReleaseChecks() From 6bc1e0d2193332cb99e3e451a4a92d41dfec9700 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 26 May 2022 20:26:47 +0800 Subject: [PATCH 027/125] clean code: remove uiauto.hh wordbyauto.hh(.cc) --- goldendict.pro | 7 +- uiauto.hh | 334 ------------------------------------------------- wordbyauto.cc | 114 ----------------- wordbyauto.hh | 6 - 4 files changed, 2 insertions(+), 459 deletions(-) delete mode 100644 uiauto.hh delete mode 100644 wordbyauto.cc delete mode 100644 wordbyauto.hh diff --git a/goldendict.pro b/goldendict.pro index 9b0906fa..853ce5ab 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -481,14 +481,11 @@ SOURCES += folding.cc \ win32 { FORMS += texttospeechsource.ui - SOURCES += wordbyauto.cc \ - guids.c \ + SOURCES += guids.c \ speechclient_win.cc \ texttospeechsource.cc \ speechhlp.cc - HEADERS += wordbyauto.hh \ - uiauto.hh \ - texttospeechsource.hh \ + HEADERS += texttospeechsource.hh \ sapi.hh \ sphelper.hh \ speechclient.hh \ diff --git a/uiauto.hh b/uiauto.hh deleted file mode 100644 index 4e40ff48..00000000 --- a/uiauto.hh +++ /dev/null @@ -1,334 +0,0 @@ -#ifndef __UIAUTO_HH_INCLUDED__ -#define __UIAUTO_HH_INCLUDED__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -EXTERN_C const IID IID_IUIAutomation; -EXTERN_C const IID CLSID_CUIAutomation; -EXTERN_C const IID IID_IUIAutomationElement; -EXTERN_C const IID IID_IUIAutomationTextPattern; -EXTERN_C const IID IID_IUIAutomationTextRange; -EXTERN_C const IID IID_IUIAutomationTreeWalker; - -typedef interface IUIAutomationElement IUIAutomationElement; -typedef interface IUIAutomationElementArray IUIAutomationElementArray; -typedef interface IUIAutomationTextPattern IUIAutomationTextPattern; -typedef interface IUIAutomationTextRange IUIAutomationTextRange; -typedef interface IUIAutomationTextRangeArray IUIAutomationTextRangeArray; -typedef interface IUIAutomationCacheRequest IUIAutomationCacheRequest; -typedef interface IUIAutomationTreeWalker IUIAutomationTreeWalker; -typedef interface IUIAutomationCondition IUIAutomationCondition; -typedef interface IUIAutomationEventHandler IUIAutomationEventHandler; -typedef interface IUIAutomationPropertyChangedEventHandler IUIAutomationPropertyChangedEventHandler; -typedef interface IUIAutomationStructureChangedEventHandler IUIAutomationStructureChangedEventHandler; -typedef interface IUIAutomationFocusChangedEventHandler IUIAutomationFocusChangedEventHandler; -typedef interface IUIAutomationProxyFactory IUIAutomationProxyFactory; -typedef interface IUIAutomationProxyFactoryEntry IUIAutomationProxyFactoryEntry; -typedef interface IUIAutomationProxyFactoryMapping IUIAutomationProxyFactoryMapping; - -typedef void *UIA_HWND; -typedef int PROPERTYID; -typedef int EVENTID; -typedef int PATTERNID; -typedef int CONTROLTYPEID; -typedef int TEXTATTRIBUTEID; - -enum TreeScope -{ - TreeScope_Element = 0x1, - TreeScope_Children = 0x2, - TreeScope_Descendants = 0x4, - TreeScope_Parent = 0x8, - TreeScope_Ancestors = 0x10, - TreeScope_Subtree = ( ( TreeScope_Element | TreeScope_Children ) | TreeScope_Descendants ) -}; - -enum PropertyConditionFlags -{ - PropertyConditionFlags_None = 0, - PropertyConditionFlags_IgnoreCase = 0x1 -}; - -enum OrientationType -{ - OrientationType_None = 0, - OrientationType_Horizontal = 1, - OrientationType_Vertical = 2 -}; - -enum SupportedTextSelection -{ - SupportedTextSelection_None = 0, - SupportedTextSelection_Single = 1, - SupportedTextSelection_Multiple = 2 -}; - -enum TextPatternRangeEndpoint -{ - TextPatternRangeEndpoint_Start = 0, - TextPatternRangeEndpoint_End = 1 -}; - -enum TextUnit -{ - TextUnit_Character = 0, - TextUnit_Format = 1, - TextUnit_Word = 2, - TextUnit_Line = 3, - TextUnit_Paragraph = 4, - TextUnit_Page = 5, - TextUnit_Document = 6 -}; - -enum ProviderOptions -{ - ProviderOptions_ClientSideProvider = 0x1, - ProviderOptions_ServerSideProvider = 0x2, - ProviderOptions_NonClientAreaProvider = 0x4, - ProviderOptions_OverrideProvider = 0x8, - ProviderOptions_ProviderOwnsSetFocus = 0x10, - ProviderOptions_UseComThreading = 0x20 -} ; - -/* UIA_PatternIds */ -const long UIA_InvokePatternId = 10000; -const long UIA_SelectionPatternId = 10001; -const long UIA_ValuePatternId = 10002; -const long UIA_RangeValuePatternId = 10003; -const long UIA_ScrollPatternId = 10004; -const long UIA_ExpandCollapsePatternId = 10005; -const long UIA_GridPatternId = 10006; -const long UIA_GridItemPatternId = 10007; -const long UIA_MultipleViewPatternId = 10008; -const long UIA_WindowPatternId = 10009; -const long UIA_SelectionItemPatternId = 10010; -const long UIA_DockPatternId = 10011; -const long UIA_TablePatternId = 10012; -const long UIA_TableItemPatternId = 10013; -const long UIA_TextPatternId = 10014; -const long UIA_TogglePatternId = 10015; -const long UIA_TransformPatternId = 10016; -const long UIA_ScrollItemPatternId = 10017; -const long UIA_LegacyIAccessiblePatternId = 10018; -const long UIA_ItemContainerPatternId = 10019; -const long UIA_VirtualizedItemPatternId = 10020; -const long UIA_SynchronizedInputPatternId = 10021; - -#ifdef INTERFACE -#undef INTERFACE -#endif - -#define INTERFACE IUIAutomation -DECLARE_INTERFACE_(IUIAutomation, IUnknown) -{ - STDMETHOD(CompareElements)(THIS_ IUIAutomationElement *, IUIAutomationElement *, BOOL *) PURE; - STDMETHOD(CompareRuntimeIds)(THIS_ SAFEARRAY *, SAFEARRAY *, BOOL *) PURE; - STDMETHOD(GetRootElement)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(ElementFromHandle)(THIS_ UIA_HWND, IUIAutomationElement **) PURE; - STDMETHOD(ElementFromPoint)(THIS_ POINT, IUIAutomationElement **) PURE; - STDMETHOD(GetFocusedElement)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(GetRootElementBuildCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(ElementFromHandleBuildCache)(THIS_ UIA_HWND, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(ElementFromPointBuildCache)(THIS_ POINT, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(GetFocusedElementBuildCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(CreateTreeWalker)(THIS_ IUIAutomationCondition *, IUIAutomationTreeWalker **) PURE; - STDMETHOD(get_ControlViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; - STDMETHOD(get_ContentViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; - STDMETHOD(get_RawViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; - STDMETHOD(get_RawViewCondition)(THIS_ IUIAutomationCondition **) PURE; - STDMETHOD(get_ControlViewCondition)(THIS_ IUIAutomationCondition **) PURE; - STDMETHOD(get_ContentViewCondition)(THIS_ IUIAutomationCondition **) PURE; - STDMETHOD(CreateCacheRequest)(THIS_ IUIAutomationCacheRequest **) PURE; - STDMETHOD(CreateTrueCondition)(THIS_ IUIAutomationCondition **) PURE; - STDMETHOD(CreateFalseCondition)(THIS_ IUIAutomationCondition **) PURE; - STDMETHOD(CreatePropertyCondition)(THIS_ PROPERTYID, VARIANT, IUIAutomationCondition **) PURE; - STDMETHOD(CreatePropertyConditionEx)(THIS_ PROPERTYID, VARIANT, enum PropertyConditionFlags, IUIAutomationCondition **) PURE; - STDMETHOD(CreateAndCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition *, IUIAutomationCondition **) PURE; - STDMETHOD(CreateAndConditionFromArray)(THIS_ SAFEARRAY *, IUIAutomationCondition **) PURE; - STDMETHOD(CreateAndConditionFromNativeArray)(THIS_ IUIAutomationCondition **, int , IUIAutomationCondition **) PURE; - STDMETHOD(CreateOrCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition *, IUIAutomationCondition **) PURE; - STDMETHOD(CreateOrConditionFromArray)(THIS_ SAFEARRAY *, IUIAutomationCondition **) PURE; - STDMETHOD(CreateOrConditionFromNativeArray)(THIS_ IUIAutomationCondition **, int , IUIAutomationCondition **) PURE; - STDMETHOD(CreateNotCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition **) PURE; - STDMETHOD(AddAutomationEventHandler)(THIS_ EVENTID, IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, IUIAutomationEventHandler *) PURE; - STDMETHOD(RemoveAutomationEventHandler)(THIS_ EVENTID, IUIAutomationElement *, IUIAutomationEventHandler *) PURE; - STDMETHOD(AddPropertyChangedEventHandlerNativeArray)(THIS_ IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, - IUIAutomationPropertyChangedEventHandler *, PROPERTYID *, int) PURE; - STDMETHOD(AddPropertyChangedEventHandler)(THIS_ IUIAutomationElement *, enum TreeScope, EVENTID, IUIAutomationCacheRequest *, - IUIAutomationPropertyChangedEventHandler *, SAFEARRAY *) PURE; - STDMETHOD(RemovePropertyChangedEventHandler)(THIS_ IUIAutomationElement *, IUIAutomationPropertyChangedEventHandler *) PURE; - STDMETHOD(AddStructureChangedEventHandler)(THIS_ IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, IUIAutomationStructureChangedEventHandler *) PURE; - STDMETHOD(RemoveStructureChangedEventHandler)(THIS_ IUIAutomationElement *, IUIAutomationStructureChangedEventHandler *) PURE; - STDMETHOD(AddFocusChangedEventHandler)(THIS_ IUIAutomationCacheRequest *, IUIAutomationFocusChangedEventHandler *) PURE; - STDMETHOD(RemoveFocusChangedEventHandler)(THIS_ IUIAutomationFocusChangedEventHandler *) PURE; - STDMETHOD(RemoveAllEventHandlers)(THIS) PURE; - STDMETHOD(IntNativeArrayToSafeArray)(THIS_ int *, int, SAFEARRAY **) PURE; - STDMETHOD(IntSafeArrayToNativeArray)(THIS_ SAFEARRAY *, int **, int *) PURE; - STDMETHOD(RectToVariant)(THIS_ RECT, VARIANT *) PURE; - STDMETHOD(VariantToRect)(THIS_ VARIANT, RECT *) PURE; - STDMETHOD(SafeArrayToRectNativeArray)(THIS_ SAFEARRAY *, RECT **, int *) PURE; - STDMETHOD(CreateProxyFactoryEntry)(THIS_ IUIAutomationProxyFactory *, IUIAutomationProxyFactoryEntry **) PURE; - STDMETHOD(get_ProxyFactoryMapping)(THIS_ IUIAutomationProxyFactoryMapping **) PURE; - STDMETHOD(GetPropertyProgrammaticName)(THIS_ PROPERTYID, BSTR *) PURE; - STDMETHOD(GetPatternProgrammaticName)(THIS_ PATTERNID, BSTR *) PURE; - STDMETHOD(PollForPotentialSupportedPatterns)(THIS_ IUIAutomationElement *, SAFEARRAY **, SAFEARRAY **) PURE; - STDMETHOD(PollForPotentialSupportedProperties)(THIS_ IUIAutomationElement *, SAFEARRAY **, SAFEARRAY **) PURE; - STDMETHOD(CheckNotSupported)(THIS_ VARIANT, BOOL *) PURE; - STDMETHOD(get_ReservedNotSupportedValue)(THIS_ IUnknown **) PURE; - STDMETHOD(get_ReservedMixedAttributeValue)(THIS_ IUnknown **) PURE; - STDMETHOD(ElementFromIAccessible)(THIS_ IAccessible *, int, IUIAutomationElement **) PURE; - STDMETHOD(ElementFromIAccessibleBuildCache)(THIS_ IAccessible *, int, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; -}; -#undef INTERFACE - -#define INTERFACE IUIAutomationElement -DECLARE_INTERFACE_(IUIAutomationElement, IUnknown) -{ - STDMETHOD(SetFocus)(THIS) PURE; - STDMETHOD(GetRuntimeId)(THIS_ SAFEARRAY **) PURE; - STDMETHOD(FindFirst)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationElement **) PURE; - STDMETHOD(FindAll)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationElementArray **) PURE; - STDMETHOD(FindFirstBuildCache)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(FindAllBuildCache)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationCacheRequest *, IUIAutomationElementArray **) PURE; - STDMETHOD(BuildUpdatedCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(GetCurrentPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; - STDMETHOD(GetCurrentPropertyValueEx)(THIS_ PROPERTYID, BOOL, VARIANT *) PURE; - STDMETHOD(GetCachedPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; - STDMETHOD(GetCachedPropertyValueEx)(THIS_ PROPERTYID, BOOL, VARIANT *) PURE; - STDMETHOD(GetCurrentPatternAs)(THIS_ PATTERNID, REFIID, void **) PURE; - STDMETHOD(GetCachedPatternAs)(THIS_ PATTERNID, REFIID, void **) PURE; - STDMETHOD(GetCurrentPattern)(THIS_ PATTERNID, IUnknown **) PURE; - STDMETHOD(GetCachedPattern)(THIS_ PATTERNID, IUnknown **) PURE; - STDMETHOD(GetCachedParent)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(GetCachedChildren)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(get_CurrentProcessId)(THIS_ int *) PURE; - STDMETHOD(get_CurrentControlType)(THIS_ CONTROLTYPEID *) PURE; - STDMETHOD(get_CurrentLocalizedControlType)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentName)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentAcceleratorKey)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentAccessKey)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentHasKeyboardFocus)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentIsKeyboardFocusable)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentIsEnabled)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentAutomationId)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentClassName)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentHelpText)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentCulture)(THIS_ int *) PURE; - STDMETHOD(get_CurrentIsControlElement)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentIsContentElement)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentIsPassword)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentNativeWindowHandle)(THIS_ UIA_HWND *) PURE; - STDMETHOD(get_CurrentItemType)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentIsOffscreen)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentOrientation)(THIS_ enum OrientationType *) PURE; - STDMETHOD(get_CurrentFrameworkId)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentIsRequiredForForm)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentItemStatus)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentBoundingRectangle)(THIS_ RECT *) PURE; - STDMETHOD(get_CurrentLabeledBy)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(get_CurrentAriaRole)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentAriaProperties)(THIS_ BSTR *) PURE; - STDMETHOD(get_CurrentIsDataValidForForm)(THIS_ BOOL *) PURE; - STDMETHOD(get_CurrentControllerFor)(THIS_ IUIAutomationElementArray **) PURE; - STDMETHOD(get_CurrentDescribedBy)(THIS_ IUIAutomationElementArray **) PURE; - STDMETHOD(get_CurrentFlowsTo)(THIS_ IUIAutomationElementArray **) PURE; - STDMETHOD(get_CurrentProviderDescription)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedProcessId)(THIS_ int *) PURE; - STDMETHOD(get_CachedControlType)(THIS_ CONTROLTYPEID *) PURE; - STDMETHOD(get_CachedLocalizedControlType)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedName)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedAcceleratorKey)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedAccessKey)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedHasKeyboardFocus)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedIsKeyboardFocusable)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedIsEnabled)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedAutomationId)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedClassName)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedHelpText)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedCulture)(THIS_ int *) PURE; - STDMETHOD(get_CachedIsControlElement)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedIsContentElement)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedIsPassword)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedNativeWindowHandle)(THIS_ UIA_HWND *) PURE; - STDMETHOD(get_CachedItemType)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedIsOffscreen)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedOrientation)(THIS_ enum OrientationType *) PURE; - STDMETHOD(get_CachedFrameworkId)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedIsRequiredForForm)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedItemStatus)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedBoundingRectangle)(THIS_ RECT *) PURE; - STDMETHOD(get_CachedLabeledBy)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(get_CachedAriaRole)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedAriaProperties)(THIS_ BSTR *) PURE; - STDMETHOD(get_CachedIsDataValidForForm)(THIS_ BOOL *) PURE; - STDMETHOD(get_CachedControllerFor)(THIS_ IUIAutomationElementArray **) PURE; - STDMETHOD(get_CachedDescribedBy)(THIS_ IUIAutomationElementArray **) PURE; - STDMETHOD(get_CachedFlowsTo)(THIS_ IUIAutomationElementArray **) PURE; - STDMETHOD(get_CachedProviderDescription)(THIS_ BSTR *) PURE; -}; -#undef INTERFACE - -#define INTERFACE IUIAutomationTextPattern -DECLARE_INTERFACE_(IUIAutomationTextPattern, IUnknown) -{ - STDMETHOD(RangeFromPoint)(THIS_ POINT, IUIAutomationTextRange **) PURE; - STDMETHOD(RangeFromChild)(THIS_ IUIAutomationElement *, IUIAutomationTextRange **) PURE; - STDMETHOD(GetSelection)(THIS_ IUIAutomationTextRangeArray **) PURE; - STDMETHOD(GetVisibleRanges)(THIS_ IUIAutomationTextRangeArray **) PURE; - STDMETHOD(get_DocumentRange)(THIS_ IUIAutomationTextRange **) PURE; - STDMETHOD(get_SupportedTextSelection)(THIS_ enum SupportedTextSelection *) PURE; -}; -#undef INTERFACE - -#define INTERFACE IUIAutomationTreeWalker -DECLARE_INTERFACE_(IUIAutomationTreeWalker, IUnknown) -{ - STDMETHOD(GetParentElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; - STDMETHOD(GetFirstChildElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; - STDMETHOD(GetLastChildElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; - STDMETHOD(GetNextSiblingElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; - STDMETHOD(GetPreviousSiblingElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; - STDMETHOD(NormalizeElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; - STDMETHOD(GetParentElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(GetFirstChildElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(GetLastChildElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(GetNextSiblingElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(GetPreviousSiblingElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(NormalizeElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; - STDMETHOD(get_Condition)(THIS_ IUIAutomationCondition **) PURE; -}; -#undef INTERFACE - -#define INTERFACE IUIAutomationTextRange -DECLARE_INTERFACE_(IUIAutomationTextRange, IUnknown) -{ - STDMETHOD(Clone)(THIS_ IUIAutomationTextRange **) PURE; - STDMETHOD(Compare)(THIS_ IUIAutomationTextRange *, BOOL *) PURE; - STDMETHOD(CompareEndpoints)(THIS_ enum TextPatternRangeEndpoint, IUIAutomationTextRange *, enum TextPatternRangeEndpoint, int *) PURE; - STDMETHOD(ExpandToEnclosingUnit)(THIS_ enum TextUnit) PURE; - STDMETHOD(FindAttribute)(THIS_ TEXTATTRIBUTEID, VARIANT, BOOL, IUIAutomationTextRange **) PURE; - STDMETHOD(FindText)(THIS_ BSTR, BOOL, BOOL, IUIAutomationTextRange **) PURE; - STDMETHOD(GetAttributeValue)(THIS_ TEXTATTRIBUTEID, VARIANT *) PURE; - STDMETHOD(GetBoundingRectangles)(THIS_ SAFEARRAY **) PURE; - STDMETHOD(GetEnclosingElement)(THIS_ IUIAutomationElement **) PURE; - STDMETHOD(GetText)(THIS_ int, BSTR *) PURE; - STDMETHOD(Move)(THIS_ enum TextUnit, int, int *) PURE; - STDMETHOD(MoveEndpointByUnit)(THIS_ enum TextPatternRangeEndpoint, enum TextUnit, int *) PURE; - STDMETHOD(MoveEndpointByRange)(THIS_ enum TextPatternRangeEndpoint, IUIAutomationTextRange *, enum TextPatternRangeEndpoint) PURE; - STDMETHOD(Select)(THIS) PURE; - STDMETHOD(AddToSelection)(THIS) PURE; - STDMETHOD(RemoveFromSelection)(THIS) PURE; - STDMETHOD(ScrollIntoView)(THIS_ BOOL) PURE; - STDMETHOD(GetChildren)(THIS_ IUIAutomationElementArray **) PURE; -}; -#undef INTERFACE - -#ifdef __cplusplus -} -#endif - -#endif // UIAUTO_HH diff --git a/wordbyauto.cc b/wordbyauto.cc deleted file mode 100644 index a3a20372..00000000 --- a/wordbyauto.cc +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include "wordbyauto.hh" -#include "uiauto.hh" - -#include -#include "gddebug.hh" - -class GDAutomationClient { -public: - GDAutomationClient(); - ~GDAutomationClient(); - bool getWordAtPoint( POINT pt ); - WCHAR *getText() { return buffer; } -private: - WCHAR buffer[256]; - IUIAutomation *pGDAutomation; - IUIAutomationTreeWalker *pTree; -}; - -GDAutomationClient gdAuto; - -GDAutomationClient::GDAutomationClient() -{ -HRESULT hr; - CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); - hr = CoCreateInstance( CLSID_CUIAutomation , NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, (void**)&pGDAutomation ); - if( hr != S_OK ) pGDAutomation = NULL; - pTree = NULL; - if( pGDAutomation != NULL ) - hr = pGDAutomation->get_RawViewWalker( &pTree ); - memset( buffer, 0, sizeof(buffer) ); -} - -GDAutomationClient::~GDAutomationClient() -{ - if( pTree != NULL ) pTree->Release(); - if( pGDAutomation != NULL ) pGDAutomation->Release(); - CoUninitialize(); -} - -bool GDAutomationClient::getWordAtPoint( POINT pt ) -{ -HRESULT hr; -IUIAutomationTextPattern *pTextPattern; -IUIAutomationTextRange *pTextRange; -IUIAutomationElement *pElement, *pParent; -BSTR bstr; -RECT r = { 0, 0, 0, 0 }; -bool bGoUp; - - GD_DPRINTF("\nEntering getWordAtPoint\n"); - - if( pGDAutomation == NULL ) return false; - - buffer[0] = 0; - pElement = NULL; - hr = pGDAutomation->ElementFromPoint( pt, &pElement ); - GD_DPRINTF("ElementFromPoint return hr=%08lX, ptr=%p\n", hr, pElement); - if( hr != S_OK || pElement == NULL ) - return false; - - pTextPattern = NULL; - bGoUp = false; - while( pElement != NULL ) { - hr = pElement->GetCurrentPatternAs( UIA_TextPatternId, IID_IUIAutomationTextPattern, (void**)&pTextPattern ); - if( hr == S_OK && pTextPattern != NULL ) - break; - if( pTree == NULL ) { - pElement->Release(); - return false; - } - pParent = NULL; - hr = pTree->GetParentElement( pElement, &pParent ); - pElement->Release(); - pElement = pParent; - bGoUp = TRUE; - } - if( pElement == NULL ) - return false; - - if( !bGoUp ) { - hr = pElement->get_CurrentBoundingRectangle( &r ); - if( hr == S_OK) { - pt.x -= r.left; - pt.y -= r.top; - } - } - pElement->Release(); - - pTextRange = NULL; - hr = pTextPattern->RangeFromPoint( pt, &pTextRange ); - pTextPattern->Release(); - if( hr != S_OK || pTextRange == NULL ) - return false; - - hr = pTextRange->ExpandToEnclosingUnit( TextUnit_Word ); - if( hr == S_OK) { - hr = pTextRange->GetText( 255, &bstr ); - if (hr == S_OK) { - wsprintfW( buffer, L"%s", (LPCWSTR)bstr ); - SysFreeString( bstr ); - } - } - pTextRange->Release(); - - return ( buffer[0] != 0 ); -} - -WCHAR *gdGetWordAtPointByAutomation( POINT pt ) -{ - if( gdAuto.getWordAtPoint( pt ) ) return gdAuto.getText(); - else return NULL; -} diff --git a/wordbyauto.hh b/wordbyauto.hh deleted file mode 100644 index 6a4cebbf..00000000 --- a/wordbyauto.hh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __WORD_BY_AUTO_HH_INCLUDED -#define __WORD_BY_AUTO_HH_INCLUDED - -WCHAR *gdGetWordAtPointByAutomation( POINT pt ); - -#endif From 96347bef7f968f07d3cc07002fe1329953f2809b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 26 May 2022 20:30:50 +0800 Subject: [PATCH 028/125] opt: print _MSC_FULL_VER instead of incorrect VS version the _MSC_FULL_VER and VS version has no direct and sound connection. --- about.cc | 6 ++---- about.hh | 14 -------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/about.cc b/about.cc index 4aa03804..5e07befa 100644 --- a/about.cc +++ b/about.cc @@ -23,10 +23,8 @@ About::About( QWidget * parent ): QDialog( parent ) ui.version->setText( version ); #if defined (_MSC_VER) - QString compilerVersion = QString( "Visual C++ %1.%2.%3" ) - .arg( GD_CXX_MSVC_MAJOR ) - .arg( GD_CXX_MSVC_MINOR ) - .arg( GD_CXX_MSVC_BUILD ); + QString compilerVersion = QString( "Visual C++ Compiler: %1" ) + .arg( _MSC_FULL_VER ); #elif defined (__clang__) && defined (__clang_version__) QString compilerVersion = QLatin1String( "Clang " ) + QLatin1String( __clang_version__ ); #else diff --git a/about.hh b/about.hh index 2376f19a..0356403c 100644 --- a/about.hh +++ b/about.hh @@ -7,20 +7,6 @@ #include "ui_about.h" #include -// Microsoft Visual C++ version -#if defined (_MSC_VER) - // how many digits does the build number have? -# if _MSC_FULL_VER / 10000 == _MSC_VER -# define GD_CXX_MSVC_BUILD (_MSC_FULL_VER % 10000) // four digits -# elif _MSC_FULL_VER / 100000 == _MSC_VER -# define GD_CXX_MSVC_BUILD (_MSC_FULL_VER % 100000) // five digits -# else -# define GD_CXX_MSVC_BUILD 0 -# endif -# define GD_CXX_MSVC_MAJOR (_MSC_VER/100-6) -# define GD_CXX_MSVC_MINOR (_MSC_VER%100) -#endif - class About: public QDialog { Q_OBJECT From f98ac1b5881911e64baec1cae2b62dd3f7368b99 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 27 May 2022 21:54:30 +0800 Subject: [PATCH 029/125] clean code:remove useless code --- articleview.cc | 35 +++-------------------------------- articleview.hh | 4 ---- 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/articleview.cc b/articleview.cc index 160d004b..f21aaf8b 100644 --- a/articleview.cc +++ b/articleview.cc @@ -708,39 +708,14 @@ void ArticleView::tryMangleWebsiteClickedUrl( QUrl & url, Contexts & contexts ) { if( framed ) { - // QVariant result = runJavaScriptSync( ui.definition->page(), "gdLastUrlText" ); - QVariant result; - - if( result.type() == QVariant::String ) - { - // Looks this way - contexts[ dictionaryIdFromScrollTo( ca ) ] = QString::fromLatin1( url.toEncoded() ); - - QUrl target; - - QString queryWord = result.toString(); - - // Empty requests are treated as no request, so we work this around by - // adding a space. - if( queryWord.isEmpty() ) - queryWord = " "; - - target.setScheme( "gdlookup" ); - target.setHost( "localhost" ); - target.setPath( "/" + queryWord ); - - url = target; - } + // no need to translate website internal url to gd builtin url + // and lack the formulation to convert them. + qDebug() << "in the website with url:" << url; } } ); } } -void ArticleView::updateCurrentArticleFromCurrentFrame( QWebEnginePage * frame ,QPoint * point) -{ - -} - void ArticleView::saveHistoryUserData() { ui.definition->setProperty("sx", ui.definition->page()->scrollPosition().x()); @@ -1100,8 +1075,6 @@ void ArticleView::linkClicked( QUrl const & url_ ) if( kmod & Qt::AltModifier ) return; - updateCurrentArticleFromCurrentFrame(); - QUrl url( url_ ); Contexts contexts; @@ -1725,8 +1698,6 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) { // Is that a link? Is there a selection? QWebEnginePage* r=ui.definition->page(); - updateCurrentArticleFromCurrentFrame(ui.definition->page(), const_cast(& pos)); - QMenu menu( this ); QAction * followLink = 0; diff --git a/articleview.hh b/articleview.hh index 658b2111..35aa99a1 100644 --- a/articleview.hh +++ b/articleview.hh @@ -380,10 +380,6 @@ private: /// url to the appropriate "contexts" entry. void tryMangleWebsiteClickedUrl( QUrl & url, Contexts & contexts ); - /// Use the known information about the current frame to update the current - /// article's value. - void updateCurrentArticleFromCurrentFrame( QWebEnginePage * frame = 0 ,QPoint * point=0); - /// Saves current article and scroll position for the current history item. /// Should be used when leaving the page. void saveHistoryUserData(); From c45b4cc255429fb05e00fda46075a48aa867e34c Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 27 May 2022 23:10:50 +0800 Subject: [PATCH 030/125] fix: invalid gdlookup url should be forbidden. --- article_netmgr.cc | 11 +++++++++++ articleview.cc | 22 ++++++++++------------ utils.hh | 22 +++++++++++++--------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index db2aa0e9..b627adc1 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -515,6 +515,17 @@ void LocalSchemeHandler::requestStarted(QWebEngineUrlRequestJob *requestJob) QNetworkRequest request; request.setUrl( url ); + //all the url reached here must be either gdlookup or bword scheme. + auto queryWord = Utils::Url::getQueryWord( url ); + auto word = queryWord.second; + // or the condition can be (!queryWord.first || word.isEmpty()) + // ( queryWord.first && word.isEmpty() ) is only part of the above condition. + if( queryWord.first && word.isEmpty() ) + { + // invalid gdlookup url. + return; + } + QNetworkReply * reply = this->mManager.getArticleReply( request ); connect( reply, &QNetworkReply::finished, requestJob, [ = ]() { requestJob->reply( "text/html", reply ); } ); connect( requestJob, &QObject::destroyed, reply, &QObject::deleteLater ); diff --git a/articleview.cc b/articleview.cc index f21aaf8b..8d5b8eae 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1107,6 +1107,14 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, audioPlayer->stop(); qDebug() << "open link url:" << url; + auto queryWord = Utils::Url::getQueryWord( url ); + auto word = queryWord.second; + if( queryWord.first && word.isEmpty() ) + { + // invalid gdlookup url. + return; + } + Contexts contexts( contexts_ ); if( url.scheme().compare( "gdpicture" ) == 0 ) @@ -1119,10 +1127,10 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QStringList dictsList = Utils::Url::queryItemValue( ref, "dictionaries" ) .split( ",", Qt::SkipEmptyParts ); - showDefinition( url.path(), dictsList, QRegExp(), getGroup( ref ), false ); + showDefinition( word, dictsList, QRegExp(), getGroup( ref ), false ); } else - showDefinition( url.path(), + showDefinition( word, getGroup( ref ), scrollTo, contexts ); } else @@ -1145,16 +1153,6 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, return; } - QString word; - - if( Utils::Url::hasQueryItem( url, "word" ) ) - { - word=Utils::Url::queryItemValue (url,"word"); - } - else{ - word=url.path ().mid (1); - } - QString newScrollTo( scrollTo ); if( Utils::Url::hasQueryItem( url, "dict" ) ) { diff --git a/utils.hh b/utils.hh index 36318167..55a255da 100644 --- a/utils.hh +++ b/utils.hh @@ -96,6 +96,7 @@ inline int loadAcquire( QAtomicInt const & ref ) namespace Url { + // This wrapper is created due to behavior change of the setPath() method // See: https://bugreports.qt-project.org/browse/QTBUG-27728 // https://codereview.qt-project.org/#change,38257 @@ -158,16 +159,15 @@ inline QString fragment( const QUrl & url ) return url.fragment( QUrl::FullyDecoded ); } -// extract query word from url -inline QString getWordFromUrl( const QUrl & url ) +// get the query word of bword and gdlookup scheme. +// if the scheme is gdlookup or scheme ,the first value of pair is true,otherwise is false; +inline std::pair< bool, QString > getQueryWord( QUrl const & url ) { QString word; - if( url.scheme().compare( "bword" ) == 0 ) - { - word = url.path(); - } - else if( url.scheme() == "gdlookup" ) // Plain html links inherit gdlookup scheme + bool validScheme = false; + if( url.scheme().compare( "gdlookup" ) == 0 ) { + validScheme = true; if( hasQueryItem( url, "word" ) ) { word = queryItemValue( url, "word" ); @@ -177,8 +177,12 @@ inline QString getWordFromUrl( const QUrl & url ) word = url.path().mid( 1 ); } } - - return word; + if( url.scheme().compare( "bword" ) == 0 ) + { + validScheme = true; + word = url.path().mid( 1 ); + } + return std::make_pair( validScheme, word ); } } From ee9530762816686863f01e9cee61d5e5e021992b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 28 May 2022 09:38:10 +0800 Subject: [PATCH 031/125] fix:remove x11extra private --- goldendict.pro | 2 +- hotkeywrapper.cc | 58 +++++++++++++++++++++++++++++++++++++++++------- hotkeywrapper.hh | 2 +- keyboardstate.cc | 12 ++++++++-- mainwindow.cc | 20 +++++++++++------ 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/goldendict.pro b/goldendict.pro index 853ce5ab..c854d055 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -124,7 +124,7 @@ unix:!mac { DEFINES += HAVE_X11 lessThan(QT_MAJOR_VERSION, 6): QT += x11extras - greaterThan(QT_MAJOR_VERSION, 5): QT += gui-private +# greaterThan(QT_MAJOR_VERSION, 5): QT += gui-private CONFIG += link_pkgconfig diff --git a/hotkeywrapper.cc b/hotkeywrapper.cc index c942253c..1a26d9a4 100644 --- a/hotkeywrapper.cc +++ b/hotkeywrapper.cc @@ -468,10 +468,17 @@ void HotkeyWrapper::init() { keyToUngrab = grabbedKeys.end(); +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif + // We use RECORD extension instead of XGrabKey. That's because XGrabKey // prevents other clients from getting their input if it's grabbed. - Display * display = QX11Info::display(); + Display * display = displayID; lShiftCode = XKeysymToKeycode( display, XK_Shift_L ); rShiftCode = XKeysymToKeycode( display, XK_Shift_R ); @@ -678,13 +685,25 @@ public: ~X11GrabUngrabErrorHandler() { - XFlush( QX11Info::display() ); +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif + XFlush( displayID ); (void) XSetErrorHandler( previousErrorHandler_ ); } bool isError() const { - XFlush( QX11Info::display() ); +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif + XFlush( displayID ); return error; } @@ -706,8 +725,14 @@ HotkeyWrapper::GrabbedKeys::iterator HotkeyWrapper::grabKey( quint32 keyCode, if ( result.second ) { +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif X11GrabUngrabErrorHandler errorHandler; - XGrabKey( QX11Info::display(), keyCode, modifiers, QX11Info::appRootWindow(), + XGrabKey( displayID, keyCode, modifiers, DefaultRootWindow(displayID), True, GrabModeAsync, GrabModeAsync ); if ( errorHandler.isError() ) @@ -722,8 +747,14 @@ HotkeyWrapper::GrabbedKeys::iterator HotkeyWrapper::grabKey( quint32 keyCode, void HotkeyWrapper::ungrabKey( GrabbedKeys::iterator i ) { +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif X11GrabUngrabErrorHandler errorHandler; - XUngrabKey( QX11Info::display(), i->first, i->second, QX11Info::appRootWindow() ); + XUngrabKey( displayID, i->first, i->second, XDefaultRootWindow(displayID) ); grabbedKeys.erase( i ); @@ -746,14 +777,25 @@ quint32 HotkeyWrapper::nativeKey(int key) keySymName = QKeySequence( key ).toString(); break; } - - Display * display = QX11Info::display(); +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif + Display * display = displayID; return XKeysymToKeycode( display, XStringToKeysym( keySymName.toLatin1().data() ) ); } void HotkeyWrapper::unregister() { - Display * display = QX11Info::display(); +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif + Display * display = displayID; XRecordDisableContext( display, recordContext ); XSync( display, False ); diff --git a/hotkeywrapper.hh b/hotkeywrapper.hh index 844458b2..00ad0965 100644 --- a/hotkeywrapper.hh +++ b/hotkeywrapper.hh @@ -10,7 +10,7 @@ #include #include #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) -#include +#include #else #include #endif diff --git a/keyboardstate.cc b/keyboardstate.cc index b6163f19..2d336ed0 100644 --- a/keyboardstate.cc +++ b/keyboardstate.cc @@ -8,7 +8,7 @@ #include #elif defined(HAVE_X11) #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) -#include +#include #else #include #endif @@ -44,9 +44,17 @@ bool KeyboardState::checkModifiersPressed( int mask ) ( mask & Shift && !( keys & ( 1 << shiftKeyBit ) ) ) || ( mask & Win && !( keys & ( 1 << controlKeyBit ) ) ) ); #else + +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif + XkbStateRec state; - XkbGetState( QX11Info::display(), XkbUseCoreKbd, &state ); + XkbGetState( displayID, XkbUseCoreKbd, &state ); return !( ( mask & Alt && !( state.base_mods & Mod1Mask ) ) || diff --git a/mainwindow.cc b/mainwindow.cc index 9876c8b4..11b523a0 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -65,7 +65,7 @@ #ifdef HAVE_X11 #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) -#include +#include #else #include #endif @@ -2903,9 +2903,15 @@ void MainWindow::toggleMainWindow( bool onlyShow ) focusTranslateLine(); #ifdef HAVE_X11 +#if QT_VERSION < 0x060000 + Display *displayID = QX11Info::display(); +#else + QNativeInterface::QX11Application *x11AppInfo = qApp->nativeInterface(); + Display *displayID = x11AppInfo->display(); +#endif Window wh = 0; int rev = 0; - XGetInputFocus( QX11Info::display(), &wh, &rev ); + XGetInputFocus( displayID, &wh, &rev ); if( wh != translateLine->internalWinId() && !byIconClick ) { QPoint p( 1, 1 ); @@ -2918,17 +2924,17 @@ void MainWindow::toggleMainWindow( bool onlyShow ) event.xbutton.x_root = p.x(); event.xbutton.y_root = p.y(); event.xbutton.window = internalWinId(); - event.xbutton.root = QX11Info::appRootWindow( QX11Info::appScreen() ); + event.xbutton.root = XDefaultRootWindow(displayID); event.xbutton.state = Button1Mask; event.xbutton.button = Button1; event.xbutton.same_screen = true; event.xbutton.time = CurrentTime; - XSendEvent( QX11Info::display(), internalWinId(), true, 0xfff, &event ); - XFlush( QX11Info::display() ); + XSendEvent( displayID, internalWinId(), true, 0xfff, &event ); + XFlush( displayID ); event.type = ButtonRelease; - XSendEvent( QX11Info::display(), internalWinId(), true, 0xfff, &event ); - XFlush( QX11Info::display() ); + XSendEvent( displayID, internalWinId(), true, 0xfff, &event ); + XFlush( displayID ); } #endif } From 7ec75d60b981db1210ada838084891e09ef3260c Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 28 May 2022 13:11:08 +0800 Subject: [PATCH 032/125] opt: refact the inspector windows logic --- article_inspect.cpp | 9 ++++----- article_inspect.h | 3 +-- mainwindow.cc | 7 ++++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/article_inspect.cpp b/article_inspect.cpp index ff296787..e194fec1 100644 --- a/article_inspect.cpp +++ b/article_inspect.cpp @@ -10,8 +10,8 @@ ArticleInspector::ArticleInspector( QWidget * parent ) : QWidget( parent, Qt::Wi QVBoxLayout * v = new QVBoxLayout( this ); v->setSpacing( 0 ); v->setContentsMargins( 0, 0, 0, 0 ); - inspectView = new QWebEngineView( this ); - v->addWidget( inspectView ); + viewContainer = new QWebEngineView( this ); + v->addWidget( viewContainer ); resize(800,600); } @@ -19,8 +19,7 @@ ArticleInspector::ArticleInspector( QWidget * parent ) : QWidget( parent, Qt::Wi void ArticleInspector::setInspectPage( QWebEngineView * view ) { auto page=view->page(); - this->inspectedPage = page; - page->setDevToolsPage( inspectView->page() ); + viewContainer->page()->setInspectedPage(page); #if( QT_VERSION > QT_VERSION_CHECK( 6, 0, 0 ) ) // without this line, application will crash on qt6.2 ,see https://bugreports.qt.io/browse/QTBUG-101724 if( view->lastContextMenuRequest() ) @@ -36,5 +35,5 @@ void ArticleInspector::setInspectPage( QWebEngineView * view ) void ArticleInspector::closeEvent( QCloseEvent * ) { - inspectedPage->setDevToolsPage( nullptr ); + viewContainer->page()->setInspectedPage(nullptr); } diff --git a/article_inspect.h b/article_inspect.h index a99d1ff2..8c7b50c5 100644 --- a/article_inspect.h +++ b/article_inspect.h @@ -9,8 +9,7 @@ class ArticleInspector : public QWidget { Q_OBJECT - QWebEngineView * inspectView = nullptr; - QWebEnginePage * inspectedPage = nullptr; + QWebEngineView * viewContainer = nullptr; public: ArticleInspector( QWidget * parent = nullptr ); diff --git a/mainwindow.cc b/mainwindow.cc index 11b523a0..2ba00eb2 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -1246,7 +1246,7 @@ void MainWindow::quitApp() { if( inspector && inspector->isVisible() ) { - inspector->hide(); + inspector->close(); } commitData(); qApp->quit(); @@ -1635,9 +1635,10 @@ ArticleView * MainWindow::createNewTab( bool switchToIt, groupList ); connect( view, &ArticleView::inspectSignal,this,[this](QWebEngineView * view){ - if(inspector){ - inspector->setInspectPage(view); + if( !inspector ){ + inspector = new ArticleInspector( this ); } + inspector->setInspectPage( view ); }); connect( view, SIGNAL( titleChanged( ArticleView *, QString const & ) ), From 7d0523b503cc326b726b392bd2bfbc49056c1d32 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 28 May 2022 16:30:18 +0800 Subject: [PATCH 033/125] clean code:remove runnable in zim.cc --- zim.cc | 69 ++++------------------------------------------------------ 1 file changed, 4 insertions(+), 65 deletions(-) diff --git a/zim.cc b/zim.cc index 15e8a5be..5fcdad4f 100644 --- a/zim.cc +++ b/zim.cc @@ -1132,32 +1132,8 @@ sptr< Dictionary::DataRequest > ZimDictionary::getSearchResults( QString const & /// ZimDictionary::getArticle() -class ZimArticleRequest; - -class ZimArticleRequestRunnable: public QRunnable -{ - ZimArticleRequest & r; - QSemaphore & hasExited; - -public: - - ZimArticleRequestRunnable( ZimArticleRequest & r_, - QSemaphore & hasExited_ ): r( r_ ), - hasExited( hasExited_ ) - {} - - ~ZimArticleRequestRunnable() - { - hasExited.release(); - } - - virtual void run(); -}; - class ZimArticleRequest: public Dictionary::DataRequest { - friend class ZimArticleRequestRunnable; - wstring word; vector< wstring > alts; ZimDictionary & dict; @@ -1173,11 +1149,10 @@ public: ZimDictionary & dict_, bool ignoreDiacritics_ ): word( word_ ), alts( alts_ ), dict( dict_ ), ignoreDiacritics( ignoreDiacritics_ ) { - QThreadPool::globalInstance()->start( - new ZimArticleRequestRunnable( *this, hasExited ) ); + QThreadPool::globalInstance()->start( [ this ]() { this->run(); } ); } - void run(); // Run from another thread by ZimArticleRequestRunnable + void run(); virtual void cancel() { @@ -1191,11 +1166,6 @@ public: } }; -void ZimArticleRequestRunnable::run() -{ - r.run(); -} - void ZimArticleRequest::run() { if ( Utils::AtomicInt::loadAcquire( isCancelled ) ) @@ -1336,32 +1306,8 @@ sptr< Dictionary::DataRequest > ZimDictionary::getArticle( wstring const & word, //// ZimDictionary::getResource() -class ZimResourceRequest; - -class ZimResourceRequestRunnable: public QRunnable -{ - ZimResourceRequest & r; - QSemaphore & hasExited; - -public: - - ZimResourceRequestRunnable( ZimResourceRequest & r_, - QSemaphore & hasExited_ ): r( r_ ), - hasExited( hasExited_ ) - {} - - ~ZimResourceRequestRunnable() - { - //hasExited.release(); - } - - virtual void run(); -}; - class ZimResourceRequest: public Dictionary::DataRequest { - friend class ZimResourceRequestRunnable; - ZimDictionary & dict; string resourceName; @@ -1372,12 +1318,10 @@ class ZimResourceRequest: public Dictionary::DataRequest public: ZimResourceRequest(ZimDictionary &dict_, string const &resourceName_) : dict(dict_), resourceName(resourceName_) { - //(new ZimResourceRequestRunnable(*this, hasExited))->run(); - QThreadPool::globalInstance()->start( - new ZimResourceRequestRunnable( *this, hasExited ) ); + QThreadPool::globalInstance()->start( [ this ]() { this->run(); } ); } - void run(); // Run from another thread by ZimResourceRequestRunnable + void run(); virtual void cancel() { @@ -1391,11 +1335,6 @@ public: } }; -void ZimResourceRequestRunnable::run() -{ - r.run(); -} - void ZimResourceRequest::run() { // Some runnables linger enough that they are cancelled before they start From 7910209ae1c175d81d972be2fdd29f6be3dd5041 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 29 May 2022 10:05:44 +0800 Subject: [PATCH 034/125] clean code: remove dsl runnable ,remove comment endline carriage --- article_maker.cc | 12 ++++---- article_netmgr.cc | 6 ++-- dsl.cc | 72 ++++------------------------------------------- goldendict.pro | 5 +--- 4 files changed, 16 insertions(+), 79 deletions(-) diff --git a/article_maker.cc b/article_maker.cc index 5df14417..41f97422 100644 --- a/article_maker.cc +++ b/article_maker.cc @@ -465,7 +465,7 @@ void ArticleRequest::altSearchFinished() if ( altSearches.empty() ) { #ifdef QT_DEBUG - qDebug( "alts finished\n" ); + qDebug( "alts finished" ); #endif // They all've finished! Now we can look up bodies @@ -534,7 +534,7 @@ void ArticleRequest::bodyFinished() if ( bodyDone ) return; - GD_DPRINTF( "some body finished\n" ); + GD_DPRINTF( "some body finished" ); bool wasUpdated = false; @@ -546,7 +546,7 @@ void ArticleRequest::bodyFinished() { // Good - GD_DPRINTF( "one finished.\n" ); + GD_DPRINTF( "one finished." ); Dictionary::DataRequest & req = *bodyRequests.front(); @@ -672,13 +672,13 @@ void ArticleRequest::bodyFinished() foundAnyDefinitions = true; } - GD_DPRINTF( "erasing..\n" ); + GD_DPRINTF( "erasing.." ); bodyRequests.pop_front(); - GD_DPRINTF( "erase done..\n" ); + GD_DPRINTF( "erase done.." ); } else { - GD_DPRINTF( "one not finished.\n" ); + GD_DPRINTF( "one not finished." ); break; } } diff --git a/article_netmgr.cc b/article_netmgr.cc index b627adc1..110f9151 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -254,9 +254,9 @@ QNetworkReply * ArticleNetworkAccessManager::getArticleReply( QNetworkRequest co sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( QUrl const & url, QString & contentType ) { - GD_DPRINTF( "getResource: %ls\n", url.toString().toStdWString().c_str() ); - GD_DPRINTF( "scheme: %ls\n", url.scheme().toStdWString().c_str() ); - GD_DPRINTF( "host: %ls\n", url.host().toStdWString().c_str() ); + GD_DPRINTF( "getResource: %ls", url.toString().toStdWString().c_str() ); + GD_DPRINTF( "scheme: %ls", url.scheme().toStdWString().c_str() ); + GD_DPRINTF( "host: %ls", url.host().toStdWString().c_str() ); if ( url.scheme() == "gdlookup" ) { diff --git a/dsl.cc b/dsl.cc index 6302feee..6125342e 100644 --- a/dsl.cc +++ b/dsl.cc @@ -1556,32 +1556,8 @@ void DslDictionary::getArticleText( uint32_t articleAddress, QString & headword, /// DslDictionary::getArticle() -class DslArticleRequest; - -class DslArticleRequestRunnable: public QRunnable -{ - DslArticleRequest & r; - QSemaphore & hasExited; - -public: - - DslArticleRequestRunnable( DslArticleRequest & r_, - QSemaphore & hasExited_ ): r( r_ ), - hasExited( hasExited_ ) - {} - - ~DslArticleRequestRunnable() - { - hasExited.release(); - } - - virtual void run(); -}; - class DslArticleRequest: public Dictionary::DataRequest { - friend class DslArticleRequestRunnable; - wstring word; vector< wstring > alts; DslDictionary & dict; @@ -1597,11 +1573,10 @@ public: DslDictionary & dict_, bool ignoreDiacritics_ ): word( word_ ), alts( alts_ ), dict( dict_ ), ignoreDiacritics( ignoreDiacritics_ ) { - QThreadPool::globalInstance()->start( - new DslArticleRequestRunnable( *this, hasExited ) ); + QThreadPool::globalInstance()->start( [ this ]() { this->run(); } ); } - void run(); // Run from another thread by DslArticleRequestRunnable + void run(); virtual void cancel() { @@ -1611,15 +1586,10 @@ public: ~DslArticleRequest() { isCancelled.ref(); - hasExited.acquire(); + //hasExited.acquire(); } }; -void DslArticleRequestRunnable::run() -{ - r.run(); -} - void DslArticleRequest::run() { if ( Utils::AtomicInt::loadAcquire( isCancelled ) ) @@ -1759,32 +1729,8 @@ sptr< Dictionary::DataRequest > DslDictionary::getArticle( wstring const & word, //// DslDictionary::getResource() -class DslResourceRequest; - -class DslResourceRequestRunnable: public QRunnable -{ - DslResourceRequest & r; - QSemaphore & hasExited; - -public: - - DslResourceRequestRunnable( DslResourceRequest & r_, - QSemaphore & hasExited_ ): r( r_ ), - hasExited( hasExited_ ) - {} - - ~DslResourceRequestRunnable() - { - hasExited.release(); - } - - virtual void run(); -}; - class DslResourceRequest: public Dictionary::DataRequest { - friend class DslResourceRequestRunnable; - DslDictionary & dict; string resourceName; @@ -1799,11 +1745,10 @@ public: dict( dict_ ), resourceName( resourceName_ ) { - QThreadPool::globalInstance()->start( - new DslResourceRequestRunnable( *this, hasExited ) ); + QThreadPool::globalInstance()->start( [ this ]() { this->run(); } ); } - void run(); // Run from another thread by DslResourceRequestRunnable + void run(); virtual void cancel() { @@ -1813,15 +1758,10 @@ public: ~DslResourceRequest() { isCancelled.ref(); - hasExited.acquire(); + //hasExited.acquire(); } }; -void DslResourceRequestRunnable::run() -{ - r.run(); -} - void DslResourceRequest::run() { // Some runnables linger enough that they are cancelled before they start diff --git a/goldendict.pro b/goldendict.pro index c854d055..f540a509 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -124,7 +124,6 @@ unix:!mac { DEFINES += HAVE_X11 lessThan(QT_MAJOR_VERSION, 6): QT += x11extras -# greaterThan(QT_MAJOR_VERSION, 5): QT += gui-private CONFIG += link_pkgconfig @@ -139,9 +138,7 @@ unix:!mac { libavcodec \ libswresample \ } - arm { - #LIBS += -liconv - } else { + !arm { LIBS += -lX11 -lXtst } From 4ac7afc6bf97d76bcb6583c0bf9dc92eb3974e27 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 29 May 2022 10:17:27 +0800 Subject: [PATCH 035/125] pro: add /permissive- option in Qt6.3+VS2022 ,the option can supresse the permissive warning. --- goldendict.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/goldendict.pro b/goldendict.pro index f540a509..f42b42c6 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -75,7 +75,8 @@ win32 { DEFINES += NOMINMAX __WIN64 } LIBS += -L$${PWD}/winlibs/lib/msvc - QMAKE_CXXFLAGS += /wd4290 /Zc:__cplusplus /std:c++17 # silence the warning C4290: C++ exception specification ignored + # silence the warning C4290: C++ exception specification ignored + QMAKE_CXXFLAGS += /wd4290 /Zc:__cplusplus /std:c++17 /permissive- # QMAKE_LFLAGS_RELEASE += /OPT:REF /OPT:ICF # QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG CONFIG+=force_debug_info From 7d43aea9b23725131e8519c4b745a31a535d5977 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 29 May 2022 12:26:39 +0800 Subject: [PATCH 036/125] opt: right context menu ,remove non-sense characters like punctuation and space and symbols --- articleview.cc | 2 +- utils.hh | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/articleview.cc b/articleview.cc index 8d5b8eae..4f997a8d 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1770,7 +1770,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) } QString selectedText = ui.definition->selectedText(); - QString text = selectedText.trimmed(); + QString text = Utils::trimNonChar( selectedText ); if ( text.size() && text.size() < 60 ) { diff --git a/utils.hh b/utils.hh index 55a255da..037bc2bf 100644 --- a/utils.hh +++ b/utils.hh @@ -28,6 +28,39 @@ inline QString rstrip(const QString &str) { return ""; } +/** + * remove punctuation , space, symbol + * + * + * " abc, '" should be "abc" + */ +inline QString trimNonChar( const QString & str ) +{ + QString remain; + int n = str.size() - 1; + for( ; n >= 0; --n ) + { + auto c = str.at( n ); + if( !c.isSpace() && !c.isSymbol() && !c.isNonCharacter() && !c.isPunct()&& !c.isNull() ) + { + remain = str.left( n + 1 ); + break; + } + } + + n = 0; + for( ; n < remain.size(); n++ ) + { + auto c = remain.at( n ); + if( !c.isSpace() && !c.isSymbol() && !c.isNonCharacter() && !c.isPunct() ) + { + return remain.mid( n ); + } + } + + return ""; +} + /** * str="abc\r\n\u0000" should be returned as "abc" * @brief rstripnull @@ -37,7 +70,8 @@ inline QString rstrip(const QString &str) { inline QString rstripnull(const QString &str) { int n = str.size() - 1; for (; n >= 0; --n) { - if (!str.at(n).isSpace()&&!str.at(n).isNull()) { + auto c = str.at(n); + if (!c.isSpace()&&!c.isNull()) { return str.left(n + 1); } } From b02d95e38c5ccb3b0f873fd786eee641d323b24d Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Sun, 29 May 2022 12:35:13 +0800 Subject: [PATCH 037/125] Update CHANGES.md --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index b5aca1dc..6067cbe8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,14 @@ ## Until to now - **CLEANING OLD/USELESS CODE** + - remove Runnable Class in dsl, zim , epwing etc files. + +- add "send to anki" +- right context menu actions, remove nonsense character like `OBJ`,punctuation etc. +- epwing remove duplicate entries when index. + + +## Until 2022-5-21 - fix a zim about:blank#block [issue](https://github.com/goldendict/goldendict/issues/1472#issuecomment-1086776611) - add fallback font family configuration for dictionary through preference dialog. - fix mdx (compact html) display error on the last item. From 9c1a22d142a9cf674df20313df2197484e10c654 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 29 May 2022 12:51:16 +0800 Subject: [PATCH 038/125] rename utils getHostBase(QUrl) to getHostBaseFromUrl --- article_netmgr.cc | 2 +- utils.hh | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index 110f9151..7ca15408 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -202,7 +202,7 @@ QNetworkReply * ArticleNetworkAccessManager::getArticleReply( QNetworkRequest co // getHostBase( refererUrl ).toUtf8().data() ); if ( !url.host().endsWith( refererUrl.host() ) && - getHostBase( url ) != getHostBase( refererUrl ) && !url.scheme().startsWith("data") ) + getHostBaseFromUrl( url ) != getHostBaseFromUrl( refererUrl ) && !url.scheme().startsWith("data") ) { gdWarning( "Blocking element \"%s\" due to not same domain", url.toEncoded().data() ); diff --git a/utils.hh b/utils.hh index 037bc2bf..bfd71a79 100644 --- a/utils.hh +++ b/utils.hh @@ -226,13 +226,6 @@ namespace { /// Uses some heuristics to chop off the first domain name from the host name, /// but only if it's not too base. Returns the resulting host name. -inline QString getHostBase( QUrl const & url ) -{ - QString host = url.host(); - - return getHostBase(host); -} - inline QString getHostBase( QString const & host ) { QStringList domains = host.split( '.' ); @@ -257,6 +250,13 @@ inline QString getHostBase( QString const & host ) else return host; } + +inline QString getHostBaseFromUrl( QUrl const & url ) +{ + QString host = url.host(); + + return getHostBase( host ); +} } #endif // UTILS_HH From 5bb2fce3c4f1088c1a2765e1df93aeb05bca193a Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 29 May 2022 16:42:04 +0800 Subject: [PATCH 039/125] fix:clang warning in macos --- dsl.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dsl.cc b/dsl.cc index 6125342e..ae1e6b42 100644 --- a/dsl.cc +++ b/dsl.cc @@ -128,11 +128,7 @@ struct InsidedCard InsidedCard( uint32_t _offset, uint32_t _size, QVector< wstring > const & words ) : offset( _offset ), size( _size ), headwords( words ) {} - InsidedCard( InsidedCard const & e ) : - offset( e.offset ), size( e.size ), headwords( e.headwords ) - {} InsidedCard() {} - }; bool indexIsOldOrBad( string const & indexFile, bool hasZipFile ) From 002a4c830229a0194f61ac1be283e4cd6e0763d7 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 30 May 2022 07:57:00 +0800 Subject: [PATCH 040/125] add debug info to release version --- goldendict.pro | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/goldendict.pro b/goldendict.pro index f42b42c6..7ff16554 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -65,6 +65,8 @@ LIBS += \ CONFIG+=utf8_source +CONFIG+=force_debug_info + win32 { TARGET = GoldenDict @@ -78,10 +80,7 @@ win32 { # silence the warning C4290: C++ exception specification ignored QMAKE_CXXFLAGS += /wd4290 /Zc:__cplusplus /std:c++17 /permissive- # QMAKE_LFLAGS_RELEASE += /OPT:REF /OPT:ICF - # QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG - CONFIG+=force_debug_info - QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO - QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO + # QMAKE_CXXFLAGS_RELEASE += /GL # slows down the linking significantly LIBS += -lshell32 -luser32 -lsapi -lole32 Debug: LIBS+= -lhunspelld From 71b5d3e6ba60103f640ae18b2d8c4d5c5e48cf87 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 30 May 2022 20:21:44 +0800 Subject: [PATCH 041/125] opt:remove runnable in ftshelper --- ftshelpers.cc | 5 ----- ftshelpers.hh | 29 ++--------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 0d775282..c1a8f08f 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -399,11 +399,6 @@ bool isCJKChar( ushort ch ) return false; } -void FTSResultsRequestRunnable::run() -{ - r.run(); -} - void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QStringList const & words, QRegExp const & searchRegexp ) diff --git a/ftshelpers.hh b/ftshelpers.hh index f8392d09..6d0fa29a 100644 --- a/ftshelpers.hh +++ b/ftshelpers.hh @@ -65,28 +65,6 @@ void makeFTSIndex( BtreeIndexing::BtreeDictionary * dict, QAtomicInt & isCancell bool isCJKChar( ushort ch ); -class FTSResultsRequest; - -class FTSResultsRequestRunnable : public QRunnable -{ - FTSResultsRequest & r; - QSemaphore & hasExited; - -public: - - FTSResultsRequestRunnable( FTSResultsRequest & r_, - QSemaphore & hasExited_ ) : r( r_ ), - hasExited( hasExited_ ) - {} - - ~FTSResultsRequestRunnable() - { - hasExited.release(); - } - - virtual void run(); -}; - class FTSResultsRequest : public Dictionary::DataRequest { BtreeIndexing::BtreeDictionary & dict; @@ -102,7 +80,6 @@ class FTSResultsRequest : public Dictionary::DataRequest int wordsInIndex; QAtomicInt isCancelled; - QSemaphore hasExited; QList< FTS::FtsHeadword > * foundHeadwords; @@ -149,11 +126,10 @@ public: searchString = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( searchString_ ) ) ); foundHeadwords = new QList< FTS::FtsHeadword >; - QThreadPool::globalInstance()->start( - new FTSResultsRequestRunnable( *this, hasExited ), -100 ); + QThreadPool::globalInstance()->start( [ this ]() { this->run(); }, -100 ); } - void run(); // Run from another thread by DslResourceRequestRunnable + void run(); virtual void cancel() { @@ -165,7 +141,6 @@ public: isCancelled.ref(); if( foundHeadwords ) delete foundHeadwords; - hasExited.acquire(); } }; From d13390befcd2119d6d63798b658b0745adecee16 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 30 May 2022 21:19:47 +0800 Subject: [PATCH 042/125] add html css 100% --- article-style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/article-style.css b/article-style.css index 903981fd..071a1dc8 100644 --- a/article-style.css +++ b/article-style.css @@ -1,5 +1,8 @@ /******** Global, non-dictionary-specific classes ***********/ +html{ + height:100%; +} body { From 5302403d8affaa2d6d035d1f96d464f39f3e5610 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 31 May 2022 20:11:04 +0800 Subject: [PATCH 043/125] add date to version --- goldendict.pro | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/goldendict.pro b/goldendict.pro index 7ff16554..8984587c 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -12,7 +12,17 @@ system(git describe --tags --always --dirty): hasGit=1 !isEmpty(hasGit){ GIT_HASH=$$system(git rev-parse --short=8 HEAD ) } -system(echo $${VERSION}.$${GIT_HASH} > version.txt) + +win32{ +# date /T output is locale aware. + DD=$$system(date /T) + DATE =$$replace(DD, / , ) +} +else{ + DATE=$$system(date '+%y%m%d') +} + +system(echo $${VERSION}.$${GIT_HASH} on $${DATE} > version.txt) # DEPENDPATH += . generators INCLUDEPATH += . From 55122eb73946787a5b6d67bf7a9251b830c59714 Mon Sep 17 00:00:00 2001 From: Abs62 Date: Tue, 31 May 2022 21:07:03 +0300 Subject: [PATCH 044/125] Zim: Handle new namespace (issue #1502) --- zim.cc | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/zim.cc b/zim.cc index 9bba8fc9..23bdc8ad 100644 --- a/zim.cc +++ b/zim.cc @@ -80,7 +80,8 @@ enum CompressionType struct ZIM_header { quint32 magicNumber; - quint32 version; + quint16 majorVersion; + quint16 minorVersion; quint8 uuid[ 16 ]; quint32 articleCount; quint32 clusterCount; @@ -127,7 +128,7 @@ __attribute__((packed)) enum { Signature = 0x584D495A, // ZIMX on little-endian, XMIZ on big-endian - CurrentFormatVersion = 1 + BtreeIndexing::FormatVersion + Folding::Version + CurrentFormatVersion = 2 + BtreeIndexing::FormatVersion + Folding::Version }; struct IdxHeader @@ -186,13 +187,25 @@ public: } const ZIM_header & header() const { return zimHeader; } + string getClusterData( quint32 cluster_nom ); + const QString getMimeType( quint16 nom ) + { return mimeTypes.value( nom ); } + + bool isArticleMime( quint16 mime_type ) + { return getMimeType( mime_type ).compare( "text/html", Qt::CaseInsensitive ) == 0 + || getMimeType( mime_type ).compare( "text/plain", Qt::CaseInsensitive ) == 0; } + + + quint16 redirectedMimeType( RedirectEntry const & redEntry ); + private: ZIM_header zimHeader; Cache cache[ CACHE_SIZE ]; int stamp; QVector< QPair< quint64, quint32 > > clusterOffsets; + QStringList mimeTypes; void clearCache(); }; @@ -293,6 +306,29 @@ bool ZimFile::open() std::sort( clusterOffsets.begin(), clusterOffsets.end() ); +// Read mime types + + string type; + char ch; + + seek( zimHeader.mimeListPos ); + + for( ; ; ) + { + type.clear(); + while( getChar( &ch ) ) + { + if( ch == 0 ) + break; + type.push_back( ch ); + } + if( type.empty() ) + break; + + QString s = QString::fromUtf8( type.c_str(), type.size() ); + mimeTypes.append( s ); + } + return true; } @@ -418,6 +454,45 @@ string ZimFile::getClusterData( quint32 cluster_nom ) return decompressedData; } +quint16 ZimFile::redirectedMimeType( RedirectEntry const & redEntry ) +{ + RedirectEntry current_entry = redEntry; + quint64 current_pos = pos(); + quint16 mimetype = 0xFFFF; + + for( ; ; ) + { + quint32 current_nom = current_entry.redirectIndex; + + seek( zimHeader.urlPtrPos + (quint64)current_nom * 8 ); + quint64 new_pos; + if( read( reinterpret_cast< char * >( &new_pos ), sizeof(new_pos) ) != sizeof(new_pos) ) + break; + + seek( new_pos ); + quint16 new_mimetype; + if( read( reinterpret_cast< char * >( &new_mimetype ), sizeof(new_mimetype) ) != sizeof(new_mimetype) ) + break; + + if( new_mimetype == 0xFFFF ) // Redirect to other article + { + if( read( reinterpret_cast< char * >( ¤t_entry ) + 2, sizeof( current_entry ) - 2 ) != sizeof( current_entry ) - 2 ) + break; + if( current_nom == current_entry.redirectIndex ) + break; + } + else + { + mimetype = new_mimetype; + break; + } + } + + seek( current_pos ); + return mimetype; +} + + // Some supporting functions bool indexIsOldOrBad( string const & indexFile ) @@ -1584,6 +1659,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( df.open(); ZIM_header const & zh = df.header(); + bool new_namespaces = ( zh.majorVersion >= 6 && zh.minorVersion >= 1 ); if( zh.magicNumber != 0x44D495A ) throw exNotZimFile( i->c_str() ); @@ -1620,7 +1696,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( } const quint64 * ptr; - quint16 mimetype; + quint16 mimetype, redirected_mime; ArticleEntry artEntry; RedirectEntry redEntry; string url, title; @@ -1637,6 +1713,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( if( ret != sizeof(RedirectEntry) - 2 ) throw exCantReadFile( i->c_str() ); + redirected_mime = df.redirectedMimeType( redEntry ); nameSpace = redEntry.nameSpace; } else @@ -1648,7 +1725,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( nameSpace = artEntry.nameSpace; - if( nameSpace == 'A' ) + if( ( nameSpace == 'A' || ( nameSpace == 'C' && new_namespaces ) ) && df.isArticleMime( mimetype ) ) articleCount++; } @@ -1671,7 +1748,8 @@ vector< sptr< Dictionary::Class > > makeDictionaries( title.push_back( ch ); } - if( nameSpace == 'A' ) + if( nameSpace == 'A' || ( nameSpace == 'C' && new_namespaces && ( df.isArticleMime( mimetype ) + || ( mimetype == 0xFFFF && df.isArticleMime( redirected_mime ) ) ) ) ) { wstring word; if( !title.empty() ) @@ -1679,16 +1757,26 @@ vector< sptr< Dictionary::Class > > makeDictionaries( else word = Utf8::decode( url ); - if( maxHeadwordsToExpand && zh.articleCount >= maxHeadwordsToExpand ) - indexedWords.addSingleWord( word, n ); + if( df.isArticleMime( mimetype ) + || ( mimetype == 0xFFFF && df.isArticleMime( redirected_mime ) ) ) + { + if( maxHeadwordsToExpand && zh.articleCount >= maxHeadwordsToExpand ) + indexedWords.addSingleWord( word, n ); + else + indexedWords.addWord( word, n ); + wordCount++; + } else - indexedWords.addWord( word, n ); - wordCount++; + { + url.insert( url.begin(), '/' ); + url.insert( url.begin(), nameSpace ); + indexedResources.addSingleWord( Utf8::decode( url ), n ); + } } else if( nameSpace == 'M' ) { - if( url.compare( "Title") == 0 ) + if( url.compare( "Title" ) == 0 ) { idxHeader.namePtr = n; string name; @@ -1696,10 +1784,10 @@ vector< sptr< Dictionary::Class > > makeDictionaries( initializing.indexingDictionary( name ); } else - if( url.compare( "Description") == 0 ) + if( url.compare( "Description" ) == 0 ) idxHeader.descriptionPtr = n; else - if( url.compare( "Language") == 0 ) + if( url.compare( "Language" ) == 0 ) { string lang; readArticle( df, n, lang ); @@ -1712,6 +1800,11 @@ vector< sptr< Dictionary::Class > > makeDictionaries( } } else + if( nameSpace == 'X' ) + { + continue; + } + else { url.insert( url.begin(), '/' ); url.insert( url.begin(), nameSpace ); From 530591ffbd795bb605651d2898943e4f94fb0771 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 1 Jun 2022 21:15:09 +0800 Subject: [PATCH 045/125] fix: add unicodeoption to qregularexpression --- epwing_book.cc | 2 +- ftshelpers.cc | 4 ++-- mainwindow.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/epwing_book.cc b/epwing_book.cc index 89aa6959..3abe85b4 100644 --- a/epwing_book.cc +++ b/epwing_book.cc @@ -857,7 +857,7 @@ bool EpwingBook::getNextHeadword( EpwingHeadword & head ) { EB_Position pos; - QRegularExpression badLinks( "#(v|n)\\d" ); + QRegularExpression badLinks( "#(v|n)\\d", QRegularExpression::UseUnicodePropertiesOption); // At first we check references queue while( !LinksQueue.isEmpty() ) diff --git a/ftshelpers.cc b/ftshelpers.cc index c1a8f08f..1b7942cb 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -71,8 +71,8 @@ bool parseSearchString( QString const & str, QStringList & indexWords, { searchWords.clear(); indexWords.clear(); - QRegularExpression spacesRegExp( "\\W+" ); - QRegularExpression wordRegExp( QString( "\\w{" ) + QString::number( FTS::MinimumWordSize ) + ",}" ); + QRegularExpression spacesRegExp( "\\W+", QRegularExpression::UseUnicodePropertiesOption ); + QRegularExpression wordRegExp( QString( "\\w{" ) + QString::number( FTS::MinimumWordSize ) + ",}", QRegularExpression::UseUnicodePropertiesOption ); QRegularExpression setsRegExp( "\\[[^\\]]+\\]", QRegularExpression::CaseInsensitiveOption ); QRegularExpression regexRegExp( "\\\\[afnrtvdDwWsSbB]|\\\\x([0-9A-Fa-f]{4})|\\\\0([0-7]{3})", QRegularExpression::CaseInsensitiveOption); diff --git a/mainwindow.cc b/mainwindow.cc index 2ba00eb2..af84c699 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -3503,7 +3503,7 @@ void MainWindow::on_saveArticle_triggered() // MDict anchors QRegularExpression anchorLinkRe( "(<\\s*a\\s+[^>]*\\b(?:name|id)\\b\\s*=\\s*[\"']*g[0-9a-f]{32}_)([0-9a-f]+_)(?=[^\"'])", - QRegularExpression::PatternOption::CaseInsensitiveOption ); + QRegularExpression::PatternOption::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption ); html.replace( anchorLinkRe, "\\1" ); if( complete ) From f9652487b9b748e776c92771b07ac524124bf009 Mon Sep 17 00:00:00 2001 From: Abs62 Date: Wed, 1 Jun 2022 17:51:56 +0300 Subject: [PATCH 046/125] Zim: A little more support for new format features --- zim.cc | 74 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/zim.cc b/zim.cc index 23bdc8ad..599ea9ea 100644 --- a/zim.cc +++ b/zim.cc @@ -128,7 +128,7 @@ __attribute__((packed)) enum { Signature = 0x584D495A, // ZIMX on little-endian, XMIZ on big-endian - CurrentFormatVersion = 2 + BtreeIndexing::FormatVersion + Folding::Version + CurrentFormatVersion = 3 + BtreeIndexing::FormatVersion + Folding::Version }; struct IdxHeader @@ -161,13 +161,15 @@ struct Cache quint32 clusterNumber; int stamp; int count, size; + unsigned blobs_offset_size; Cache() : data( 0 ), clusterNumber( 0 ), stamp( -1 ), count( 0 ), - size( 0 ) + size( 0 ), + blobs_offset_size( 0 ) {} }; @@ -188,14 +190,14 @@ public: const ZIM_header & header() const { return zimHeader; } - string getClusterData( quint32 cluster_nom ); + string getClusterData( quint32 cluster_nom, unsigned & blob_offset_size ); const QString getMimeType( quint16 nom ) { return mimeTypes.value( nom ); } bool isArticleMime( quint16 mime_type ) - { return getMimeType( mime_type ).compare( "text/html", Qt::CaseInsensitive ) == 0 - || getMimeType( mime_type ).compare( "text/plain", Qt::CaseInsensitive ) == 0; } + { return getMimeType( mime_type ).startsWith( "text/html", Qt::CaseInsensitive ) + || getMimeType( mime_type ).startsWith( "text/plain", Qt::CaseInsensitive ); } quint16 redirectedMimeType( RedirectEntry const & redEntry ); @@ -332,7 +334,7 @@ bool ZimFile::open() return true; } -string ZimFile::getClusterData( quint32 cluster_nom ) +string ZimFile::getClusterData( quint32 cluster_nom, unsigned & blobs_offset_size ) { // Check cache int target = 0; @@ -366,6 +368,7 @@ string ZimFile::getClusterData( quint32 cluster_nom ) if( found ) { // Cache hit + blobs_offset_size = cache[ target ].blobs_offset_size; return string( cache[ target ].data, cache[ target ].count ); } @@ -391,9 +394,11 @@ string ZimFile::getClusterData( quint32 cluster_nom ) seek( clusterOffsets.at( nom ).first ); - char compressionType; - if( !getChar( &compressionType ) ) + char compressionType, cluster_info; + if( !getChar( &cluster_info ) ) return string(); + compressionType = cluster_info & 0x0F; + blobs_offset_size = cluster_info & 0x10 && zimHeader.majorVersion >= 6 ? 8 : 4; string decompressedData; @@ -422,9 +427,16 @@ string ZimFile::getClusterData( quint32 cluster_nom ) // Check BLOBs number in the cluster // We cache multi-element clusters only - quint32 firstOffset; - memcpy( &firstOffset, decompressedData.data(), sizeof(firstOffset) ); - quint32 blobCount = ( firstOffset - 4 ) / 4; + quint32 firstOffset32; + quint64 firstOffset; + if( blobs_offset_size == 8 ) + memcpy( &firstOffset, decompressedData.data(), sizeof(firstOffset) ); + else + { + memcpy( &firstOffset32, decompressedData.data(), sizeof(firstOffset32) ); + firstOffset = firstOffset32; + } + quint32 blobCount = ( firstOffset - blobs_offset_size ) / blobs_offset_size; if( blobCount > 1 ) { @@ -448,6 +460,7 @@ string ZimFile::getClusterData( quint32 cluster_nom ) memcpy( cache[ target ].data, decompressedData.c_str(), size ); cache[ target ].count = size; cache[ target ].clusterNumber = cluster_nom; + cache[ target ].blobs_offset_size = blobs_offset_size; } } @@ -593,23 +606,42 @@ quint32 readArticle( ZimFile & file, quint32 articleNumber, string & result, // Read cluster data - string decompressedData = file.getClusterData( artEntry.clusterNumber ); + unsigned offset_size = 0; + string decompressedData = file.getClusterData( artEntry.clusterNumber, offset_size ); if( decompressedData.empty() ) break; // Take article data from cluster - quint32 firstOffset; - memcpy( &firstOffset, decompressedData.data(), sizeof(firstOffset) ); - quint32 blobCount = ( firstOffset - 4 ) / 4; + quint32 firstOffset32; + quint64 firstOffset; + + if( offset_size == 8 ) + memcpy( &firstOffset, decompressedData.data(), sizeof(firstOffset) ); + else + { + memcpy( &firstOffset32, decompressedData.data(), sizeof(firstOffset32) ); + firstOffset = firstOffset32; + } + quint32 blobCount = ( firstOffset - offset_size ) / offset_size; if( artEntry.blobNumber > blobCount ) break; - quint32 offsets[ 2 ]; - memcpy( offsets, decompressedData.data() + artEntry.blobNumber * 4, sizeof(offsets) ); - quint32 size = offsets[ 1 ] - offsets[ 0 ]; - - result.append( decompressedData, offsets[ 0 ], size ); + quint32 size; + if( offset_size == 8 ) + { + quint64 offsets[ 2 ]; + memcpy( offsets, decompressedData.data() + artEntry.blobNumber * 8, sizeof(offsets) ); + size = offsets[ 1 ] - offsets[ 0 ]; + result.append( decompressedData, offsets[ 0 ], size ); + } + else + { + quint32 offsets[ 2 ]; + memcpy( offsets, decompressedData.data() + artEntry.blobNumber * 4, sizeof(offsets) ); + size = offsets[ 1 ] - offsets[ 0 ]; + result.append( decompressedData, offsets[ 0 ], size ); + } return articleNumber; } @@ -1696,7 +1728,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( } const quint64 * ptr; - quint16 mimetype, redirected_mime; + quint16 mimetype, redirected_mime = 0xFFFF; ArticleEntry artEntry; RedirectEntry redEntry; string url, title; From 8acec2e0628b1c2e1ec80a2ef06c0d16af44bbc0 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Fri, 27 May 2022 11:52:16 +0300 Subject: [PATCH 047/125] Reduce build log verbosity The default qmake build output is overly verbose. Adding the "silent" switch to CONFIG makes it much more concise, comparable to default CMake output. This way warnings and errors are much easier to find. Adding CONFIG-=silent to the qmake command line doesn't restore the verbosity because it is applied before the "CONFIG += silent" line in goldendict.pro. Add a new CONFIG switch "verbose_build_output" to allow increasing build log verbosity without editing goldendict.pro. --- goldendict.pro | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/goldendict.pro b/goldendict.pro index 4a4a3094..5223e933 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -14,6 +14,14 @@ isEmpty( hasGit ) { system(echo $$VERSION > version.txt) } +!CONFIG( verbose_build_output ) { + !win32|*-msvc* { + # Reduce build log verbosity except for MinGW builds (mingw-make cannot + # execute "@echo ..." commands inserted by qmake). + CONFIG += silent + } +} + # DEPENDPATH += . generators INCLUDEPATH += . From aa33b8bebc913f923f56c56ae6ac41c163e90125 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Wed, 1 Jun 2022 18:39:24 +0300 Subject: [PATCH 048/125] Disable assert macro checks in Release builds --- goldendict.pro | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/goldendict.pro b/goldendict.pro index 5223e933..241745f6 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -22,6 +22,10 @@ isEmpty( hasGit ) { } } +CONFIG( release, debug|release ) { + DEFINES += NDEBUG +} + # DEPENDPATH += . generators INCLUDEPATH += . From f015ff555f9db5c535fafab40329122a35ac16af Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 1 Jun 2022 23:11:41 +0800 Subject: [PATCH 049/125] feature: save bookmark to favorite panel reuse fulltext match to implement this bookmark feature --- articleview.cc | 81 +++++++++++++++++++++++++++++++++----------------- articleview.hh | 11 ++++++- mainwindow.cc | 28 +++++++++++++++-- mainwindow.hh | 2 ++ 4 files changed, 92 insertions(+), 30 deletions(-) diff --git a/articleview.cc b/articleview.cc index 4f997a8d..2df8b49e 100644 --- a/articleview.cc +++ b/articleview.cc @@ -402,6 +402,9 @@ void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned g if ( scrollTo.size() ) Utils::Url::addQueryItem( req, "scrollto", scrollTo ); + if(delayedHighlightText.size()) + Utils::Url::addQueryItem( req, "regexp", delayedHighlightText ); + Contexts::Iterator pos = contexts.find( "gdanchor" ); if( pos != contexts.end() ) { @@ -579,6 +582,12 @@ void ArticleView::loadFinished( bool result ) } if( Utils::Url::hasQueryItem( ui.definition->url(), "regexp" ) ) highlightFTSResults(); + + if( !delayedHighlightText.isEmpty() ) + { + // findText( delayedHighlightText, QWebEnginePage::FindCaseSensitively ,[](bool){}); + delayedHighlightText.clear(); + } } void ArticleView::loadProgress(int ){ @@ -1592,6 +1601,11 @@ void ArticleView::setSelectionBySingleClick( bool set ) ui.definition->setSelectionBySingleClick( set ); } +void ArticleView::setDelayedHighlightText(QString const & text) +{ + delayedHighlightText = text; +} + void ArticleView::back() { // Don't allow navigating back to page 0, which is usually the initial @@ -1712,6 +1726,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) QAction * sendWordToInputLineAction = 0; QAction * saveImageAction = 0; QAction * saveSoundAction = 0; + QAction * saveBookmark = 0; #if( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) const QWebEngineContextMenuData * menuData = &(r->contextMenuData()); @@ -1832,6 +1847,12 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) } } + if(text.size()) + { + saveBookmark = new QAction( tr( "Save &Bookmark \"%1\"" ).arg( text.left( 60 ) ), &menu ); + menu.addAction( saveBookmark ); + } + // add anki menu if( !text.isEmpty() && cfg.preferences.ankiConnectServer.enabled ) { @@ -1932,6 +1953,10 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) else if ( result == lookupSelection ) showDefinition( selectedText, getGroup( ui.definition->url() ), getCurrentArticle() ); + else if( result == saveBookmark ) + { + emit saveBookmarkSignal( selectedText ); + } else if( result == sendToAnkiAction ) { sendToAnki( ui.definition->title(), ui.definition->selectedText() ); @@ -2333,42 +2358,44 @@ void ArticleView::performFindOperation( bool restart, bool backwards, bool check if ( backwards ) f |= QWebEnginePage::FindBackward; - bool setMark = text.size() && !findText(text, f); + findText( text, + f, + [ &text, this ]( bool match ) + { + bool setMark = !text.isEmpty() && !match; - if ( ui.searchText->property( "noResults" ).toBool() != setMark ) - { - ui.searchText->setProperty( "noResults", setMark ); + if( ui.searchText->property( "noResults" ).toBool() != setMark ) + { + ui.searchText->setProperty( "noResults", setMark ); - // Reload stylesheet - reloadStyleSheet(); - } + // Reload stylesheet + reloadStyleSheet(); + } + } ); } -bool ArticleView::findText(QString& text, const QWebEnginePage::FindFlags& f) +void ArticleView::findText( QString & text, + const QWebEnginePage::FindFlags & f, + const std::function< void( bool match ) > & callback ) { - bool r; - // turn async to sync invoke. - QSharedPointer loop = QSharedPointer(new QEventLoop()); - QTimer::singleShot(1000, loop.data(), &QEventLoop::quit); -#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) - ui.definition->findText(text, f, [&](const QWebEngineFindTextResult& result) +#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) + ui.definition->findText( text, + f, + [ callback ]( const QWebEngineFindTextResult & result ) { - if(loop->isRunning()){ - r = result.numberOfMatches()>0; - loop->quit(); - } }); + auto r = result.numberOfMatches() > 0; + if( callback ) + callback( r ); + } ); #else - ui.definition->findText(text, f, [&](bool result) + ui.definition->findText( text, + f, + [ callback ]( bool result ) { - if(loop->isRunning()){ - r = result; - loop->quit(); - } }); + if( callback ) + callback( result ); + } ); #endif - - - loop->exec(); - return r; } void ArticleView::reloadStyleSheet() diff --git a/articleview.hh b/articleview.hh index 35aa99a1..e2a570d0 100644 --- a/articleview.hh +++ b/articleview.hh @@ -79,6 +79,8 @@ class ArticleView: public QFrame bool ftsSearchIsOpened, ftsSearchMatchCase; int ftsPosition; + QString delayedHighlightText; + void highlightFTSResults(); void highlightAllFtsOccurences( QWebEnginePage::FindFlags flags ); void performFtsFindOperation( bool backwards ); @@ -157,6 +159,8 @@ public: /// Called when preference changes void setSelectionBySingleClick( bool set ); + void setDelayedHighlightText(QString const & text); + public slots: /// Goes back in history @@ -227,6 +231,10 @@ public: ResourceToSaveHandler * saveResource( const QUrl & url, const QString & fileName ); ResourceToSaveHandler * saveResource( const QUrl & url, const QUrl & ref, const QString & fileName ); + void findText( QString & text, + const QWebEnginePage::FindFlags & f, + const std::function< void( bool match ) > & callback = nullptr ); + signals: void iconChanged( ArticleView *, QIcon const & icon ); @@ -285,6 +293,8 @@ signals: void inspectSignal(QWebEngineView * view); + void saveBookmarkSignal( const QString & bookmark ); + public slots: void on_searchPrevious_clicked(); @@ -391,7 +401,6 @@ private: void performFindOperation( bool restart, bool backwards, bool checkHighlight = false ); - bool findText(QString& text, const QWebEnginePage::FindFlags& f); void reloadStyleSheet(); diff --git a/mainwindow.cc b/mainwindow.cc index af84c699..f8103eae 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -1690,6 +1690,7 @@ ArticleView * MainWindow::createNewTab( bool switchToIt, connect( view, SIGNAL( zoomIn()), this, SLOT( zoomin() ) ); connect( view, SIGNAL( zoomOut()), this, SLOT( zoomout() ) ); + connect( view, &ArticleView::saveBookmarkSignal, this, &MainWindow::addBookmarkToFavorite ); view->setSelectionBySingleClick( cfg.preferences.selectWordBySingleClick ); @@ -4656,6 +4657,15 @@ void MainWindow::addWordToFavorites( QString const & word, unsigned groupId ) ui.favoritesPaneWidget->addHeadword( folder, word ); } +void MainWindow::addBookmarkToFavorite( QString const & text ) +{ + // get current tab word. + QString word = unescapeTabHeader( ui.tabWidget->tabText( ui.tabWidget->currentIndex() ) ); + const auto bookmark = QString( "%1~~~%2" ).arg( word, text ); + + ui.favoritesPaneWidget->addHeadword( nullptr, bookmark ); +} + void MainWindow::addAllTabsToFavorites() { QString folder; @@ -4728,8 +4738,22 @@ void MainWindow::headwordFromFavorites( QString const & headword, } // Show headword without lost of focus on Favorites tree - setTranslateBoxTextAndClearSuffix( headword, EscapeWildcards, DisablePopup ); - showTranslationFor(headword ); + // bookmark cases: the favorite item may like this "word~~~selectedtext" + auto words = headword.split( "~~~" ); + + setTranslateBoxTextAndClearSuffix( words[0], EscapeWildcards, DisablePopup ); + + //must be a bookmark. + if(words.size()>1) + { + auto view = getCurrentArticleView(); + if(view) + { + view->setDelayedHighlightText(words[1]);// findText( words[ 1 ], QWebEnginePage::FindCaseSensitively ); + } + } + + showTranslationFor( words[ 0 ] ); } #ifdef Q_OS_WIN32 diff --git a/mainwindow.hh b/mainwindow.hh index 4e3e4831..882dc1ae 100644 --- a/mainwindow.hh +++ b/mainwindow.hh @@ -462,6 +462,8 @@ private slots: void addWordToFavorites( QString const & word, unsigned groupId ); + void addBookmarkToFavorite( QString const & text ); + bool isWordPresentedInFavorites( QString const & word, unsigned groupId ); void sendWordToInputLine( QString const & word ); From 99982a1c1127035e98ae60cf47f5a987f59a9dfc Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Thu, 2 Jun 2022 10:51:30 +0300 Subject: [PATCH 050/125] ArticleView: expose only minimal API to JavaScript https://doc.qt.io/archives/qt-5.5/qtwebkit-bridge.html#internet-security Qt WebKit Bridge documentation recommends: When exposing native objects to an open web environment, it is important to understand the security implications. Think whether the exposed object enables the web environment access things that shouldn't be open, and whether the web content loaded by that web page comes from a trusted source. The author of Qt WebChannel has said the following in a talk that introduced this Qt module (WebKit Bridge replacement for Qt WebEngine): My suggestion here is to write dedicated QObjects with a slim, minimal API that only have the signals and methods that you deem safe to be used from the outside. - see a comment under https://redirect.invidious.io/watch?v=KnvnTi6XafA --- articleview.cc | 23 ++++++++++++++++++++++- articleview.hh | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/articleview.cc b/articleview.cc index 781e700e..01711cbc 100644 --- a/articleview.cc +++ b/articleview.cc @@ -50,6 +50,24 @@ using std::map; using std::list; +/// This class exposes only slim, minimal API to JavaScript clients in order to +/// reduce attack surface available to potentionally malicious external scripts. +class ArticleViewJsProxy: public QObject +{ + Q_OBJECT +public: + /// Note: view becomes the parent of this proxy object. + explicit ArticleViewJsProxy( ArticleView & view ): + QObject( &view ), articleView( view ) + {} + + Q_INVOKABLE void onJsActiveArticleChanged( QString const & id ) + { articleView.onJsActiveArticleChanged( id ); } + +private: + ArticleView & articleView; +}; + /// AccentMarkHandler class /// /// Remove accent marks from text @@ -222,6 +240,7 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, groups( groups_ ), popupView( popupView_ ), cfg( cfg_ ), + jsProxy( new ArticleViewJsProxy( *this ) ), pasteAction( this ), articleUpAction( this ), articleDownAction( this ), @@ -1140,7 +1159,7 @@ void ArticleView::linkHovered ( const QString & link, const QString & , const QS void ArticleView::attachToJavaScript() { - ui.definition->page()->mainFrame()->addToJavaScriptWindowObject( QString( "articleview" ), this ); + ui.definition->page()->mainFrame()->addToJavaScriptWindowObject( "articleview", jsProxy ); } void ArticleView::linkClicked( QUrl const & url_ ) @@ -3090,3 +3109,5 @@ void ResourceToSaveHandler::downloadFinished() deleteLater(); } } + +#include "articleview.moc" diff --git a/articleview.hh b/articleview.hh index 8512d821..484c2d5f 100644 --- a/articleview.hh +++ b/articleview.hh @@ -15,6 +15,7 @@ #include "groupcombobox.hh" #include "ui_articleview.h" +class ArticleViewJsProxy; class ResourceToSaveHandler; /// A widget with the web view tailored to view and handle articles -- it @@ -32,6 +33,8 @@ class ArticleView: public QFrame Ui::ArticleView ui; + ArticleViewJsProxy * const jsProxy; + QAction pasteAction, articleUpAction, articleDownAction, goBackAction, goForwardAction, selectCurrentArticleAction, copyAsTextAction, inspectAction; From 66499007f6208736f6867ccd0d0c7eab5f238aa9 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 1 Jun 2022 23:40:00 +0800 Subject: [PATCH 051/125] clean code: remove \n in parameter of GD_DPRINTF --- article_netmgr.cc | 7 ++----- articleview.cc | 7 ++++--- indexedzip.cc | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index 7ca15408..6d0d3f5c 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -198,9 +198,6 @@ QNetworkReply * ArticleNetworkAccessManager::getArticleReply( QNetworkRequest co QUrl refererUrl = QUrl::fromEncoded( referer ); - //GD_DPRINTF( "Considering %s vs %s\n", getHostBase( req.url() ).toUtf8().data(), - // getHostBase( refererUrl ).toUtf8().data() ); - if ( !url.host().endsWith( refererUrl.host() ) && getHostBaseFromUrl( url ) != getHostBaseFromUrl( refererUrl ) && !url.scheme().startsWith("data") ) { @@ -449,7 +446,7 @@ qint64 ArticleResourceReply::readData( char * out, qint64 maxSize ) qint64 left = avail - alreadyRead; qint64 toRead = maxSize < left ? maxSize : left; - GD_DPRINTF( "====reading %d bytes\n", (int)toRead ); + GD_DPRINTF( "====reading %d bytes", (int)toRead ); try { @@ -457,7 +454,7 @@ qint64 ArticleResourceReply::readData( char * out, qint64 maxSize ) } catch( std::exception & e ) { - qWarning( "getDataSlice error: %s\n", e.what() ); + qWarning( "getDataSlice error: %s", e.what() ); } alreadyRead += toRead; diff --git a/articleview.cc b/articleview.cc index 2df8b49e..a5443044 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1849,7 +1849,8 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) if(text.size()) { - saveBookmark = new QAction( tr( "Save &Bookmark \"%1\"" ).arg( text.left( 60 ) ), &menu ); + // avoid too long in the menu ,use left 30 characters. + saveBookmark = new QAction( tr( "Save &Bookmark \"%1...\"" ).arg( text.left( 30 ) ), &menu ); menu.addAction( saveBookmark ); } @@ -1952,10 +1953,10 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) QDesktopServices::openUrl( targetUrl ); else if ( result == lookupSelection ) - showDefinition( selectedText, getGroup( ui.definition->url() ), getCurrentArticle() ); + showDefinition( text, getGroup( ui.definition->url() ), getCurrentArticle() ); else if( result == saveBookmark ) { - emit saveBookmarkSignal( selectedText ); + emit saveBookmarkSignal( text.left( 60 ) ); } else if( result == sendToAnkiAction ) { diff --git a/indexedzip.cc b/indexedzip.cc index c5b77713..438700eb 100644 --- a/indexedzip.cc +++ b/indexedzip.cc @@ -64,7 +64,7 @@ bool IndexedZip::loadFile( uint32_t offset, vector< char > & data ) if ( !ZipFile::readLocalHeader( zip, header ) ) { - GD_DPRINTF( "Failed to load header\n" ); + GD_DPRINTF( "Failed to load header" ); return false; } @@ -73,13 +73,13 @@ bool IndexedZip::loadFile( uint32_t offset, vector< char > & data ) switch( header.compressionMethod ) { case ZipFile::Uncompressed: - GD_DPRINTF( "Uncompressed\n" ); + GD_DPRINTF( "Uncompressed" ); data.resize( header.uncompressedSize ); return (size_t) zip.read( &data.front(), data.size() ) == data.size(); case ZipFile::Deflated: { - GD_DPRINTF( "Deflated\n" ); + GD_DPRINTF( "Deflated" ); // Now do the deflation From 235bd8dc571a1aa03290bc95da70f97df5182202 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 2 Jun 2022 20:32:31 +0800 Subject: [PATCH 052/125] add translation to save bookmark action --- locale/zh_CN.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locale/zh_CN.ts b/locale/zh_CN.ts index 832cea28..1fbe59c3 100644 --- a/locale/zh_CN.ts +++ b/locale/zh_CN.ts @@ -329,7 +329,12 @@ 引用的音频播放程序不存在。 - + + Save &Bookmark "%1..." + 保存为书签(&S)“%1...” + + + &Send "%1" to anki with selected text. 将“%1”发送到anki并附带选择的文本。 From 5fdfaa5aec351182f603c1b4eebeb9b8d79d29dd Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 2 Jun 2022 20:38:55 +0800 Subject: [PATCH 053/125] remove uselesscode the gdDebug gdWarning method, set the QTextCodec, does not seem to take effect --- gddebug.cc | 49 +++++++++---------------------------------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/gddebug.cc b/gddebug.cc index 9fce9dd3..59958bc0 100644 --- a/gddebug.cc +++ b/gddebug.cc @@ -4,61 +4,30 @@ #include #include "gddebug.hh" #include -#if(QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include #else #include #endif QFile * logFilePtr; -static QTextCodec * utf8Codec; -void gdWarning(const char *msg, ...) +void gdWarning( const char * msg, ... ) { -va_list ap; -va_start(ap, msg); -QTextCodec *localeCodec = 0; - - if( logFilePtr && logFilePtr->isOpen() ) - { - if( utf8Codec == 0 ) - utf8Codec = QTextCodec::codecForName( "UTF8" ); - - localeCodec = QTextCodec::codecForLocale(); - QTextCodec::setCodecForLocale( utf8Codec ); - } + va_list ap; + va_start( ap, msg ); qWarning() << QString().vasprintf( msg, ap ); - if( logFilePtr && logFilePtr->isOpen() ) - { - QTextCodec::setCodecForLocale( localeCodec ); - } - - va_end(ap); + va_end( ap ); } -void gdDebug(const char *msg, ...) +void gdDebug( const char * msg, ... ) { -va_list ap; -va_start(ap, msg); -// QTextCodec *localeCodec = 0; - - // if( logFilePtr && logFilePtr->isOpen() ) - // { - // if( utf8Codec == 0 ) - // utf8Codec = QTextCodec::codecForName( "UTF8" ); - - // localeCodec = QTextCodec::codecForLocale(); - // QTextCodec::setCodecForLocale( utf8Codec ); - // } + va_list ap; + va_start( ap, msg ); qDebug().noquote() << QString().vasprintf( msg, ap ); - // if( logFilePtr && logFilePtr->isOpen() ) - // { - // QTextCodec::setCodecForLocale( localeCodec ); - // } - - va_end(ap); + va_end( ap ); } From b5d005d3eddc0b9cc25e6127048ed7349169a7af Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 12:32:27 +0800 Subject: [PATCH 054/125] opt: clear the delayedHighlight text earlier --- articleview.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/articleview.cc b/articleview.cc index a5443044..e9e90d55 100644 --- a/articleview.cc +++ b/articleview.cc @@ -402,8 +402,11 @@ void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned g if ( scrollTo.size() ) Utils::Url::addQueryItem( req, "scrollto", scrollTo ); - if(delayedHighlightText.size()) + if( delayedHighlightText.size() ) + { Utils::Url::addQueryItem( req, "regexp", delayedHighlightText ); + delayedHighlightText.clear(); + } Contexts::Iterator pos = contexts.find( "gdanchor" ); if( pos != contexts.end() ) @@ -582,12 +585,6 @@ void ArticleView::loadFinished( bool result ) } if( Utils::Url::hasQueryItem( ui.definition->url(), "regexp" ) ) highlightFTSResults(); - - if( !delayedHighlightText.isEmpty() ) - { - // findText( delayedHighlightText, QWebEnginePage::FindCaseSensitively ,[](bool){}); - delayedHighlightText.clear(); - } } void ArticleView::loadProgress(int ){ From c397af52419b8b67775a4d0c1ff6277b504da187 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 12:39:05 +0800 Subject: [PATCH 055/125] style: minor change .mdict css style remove .mdict's margin-top: 1em add .gddictnamebodyseparator with clear:both --- article-style-st-lingoes.css | 3 +-- article-style.css | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/article-style-st-lingoes.css b/article-style-st-lingoes.css index fdae35d1..9f31b39e 100644 --- a/article-style-st-lingoes.css +++ b/article-style-st-lingoes.css @@ -95,7 +95,6 @@ body .gddictnamebodyseparator { - display: inline-block; - width: 100%; + clear: both; border-top: 1px solid #92b0dd; } diff --git a/article-style.css b/article-style.css index 071a1dc8..d53beb1e 100644 --- a/article-style.css +++ b/article-style.css @@ -41,6 +41,11 @@ pre /*background: #ffffdd;*/ } +.gddictnamebodyseparator +{ + clear: both; +} + /* The 'From ' string which precedes dictionary name in the heading */ .gdfromprefix { @@ -525,7 +530,7 @@ div.xdxf /************* MDict dictionaries **************/ .mdict { - margin-top: 1em; + /* margin-top: 1em; */ } .mdict a[name] From 7f62859b1f3c1615a56114748140ec62f88c4135 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 12:41:13 +0800 Subject: [PATCH 056/125] style: content-intrinsic-size increase to 600px --- article-style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/article-style.css b/article-style.css index d53beb1e..0cd9103c 100644 --- a/article-style.css +++ b/article-style.css @@ -63,8 +63,8 @@ pre background: #fefdeb; /*fix for invalid blg*/ font-style:normal; - content-visibility:auto; - contain-intrinsic-size:400px; + content-visibility: auto; + contain-intrinsic-size: 600px; } /* CSS trick to prevent the floating elements to overflow From 0eb89a68cb589119dda74aabd47bbd7eebb8e03f Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 12:50:59 +0800 Subject: [PATCH 057/125] pro file add date to version --- goldendict.pro | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/goldendict.pro b/goldendict.pro index d7fd2c34..440dda9a 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -15,11 +15,10 @@ system(git describe --tags --always --dirty): hasGit=1 win32{ # date /T output is locale aware. - DD=$$system(date /T) - DATE =$$replace(DD, / , ) + DATE=$$system(date /T) } else{ - DATE=$$system(date '+%y%m%d') + DATE=$$system(date '+%Y/%m/%d') } system(echo $${VERSION}.$${GIT_HASH} on $${DATE} > version.txt) From 80021a6328820c7a59238d48020302630f713b9f Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 13:09:18 +0800 Subject: [PATCH 058/125] opt: findText will clear the last findText , so this line is useless it will override by the following findText --- articleview.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articleview.cc b/articleview.cc index e9e90d55..b1eace83 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2569,7 +2569,7 @@ void ArticleView::highlightFTSResults() if( !allMatches.isEmpty() ) { - highlightAllFtsOccurences( flags ); +// highlightAllFtsOccurences( flags ); ui.definition->findText( allMatches.at( 0 ), flags ); // if( ui.definition->findText( allMatches.at( 0 ), flags ) ) { From e26db104f710d0c057d657e4e82c12cd53082e9e Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 15:33:16 +0800 Subject: [PATCH 059/125] fix: regression introduce by replacing qBinaryFind --- btreeidx.cc | 2 +- fulltextsearch.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/btreeidx.cc b/btreeidx.cc index 39dad39a..0ce035cb 100644 --- a/btreeidx.cc +++ b/btreeidx.cc @@ -1402,7 +1402,7 @@ void BtreeIndex::getHeadwordsFromOffsets( QList & offsets, QList::Iterator it = std::lower_bound( begOffsets, endOffsets, articleOffset ); - if( it!=offsets.end()) + if( it != offsets.end() && *it == articleOffset ) { if( isCancelled && Utils::AtomicInt::loadAcquire( *isCancelled ) ) return; diff --git a/fulltextsearch.cc b/fulltextsearch.cc index 7ad1e5d2..57842817 100644 --- a/fulltextsearch.cc +++ b/fulltextsearch.cc @@ -621,7 +621,7 @@ Q_UNUSED( parent ); for( int x = 0; x < hws.length(); x++ ) { QList< FtsHeadword >::iterator it = std::lower_bound( headwords.begin(), headwords.end(), hws.at( x ) ); - if( it != headwords.end() ) + if( it != headwords.end() && *it == hws.at( x ) ) { it->dictIDs.push_back( hws.at( x ).dictIDs.front() ); for( QStringList::const_iterator itr = it->foundHiliteRegExps.constBegin(); From 647041cec3efa79394670795bc73015a8c02f6e5 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 15:19:58 +0800 Subject: [PATCH 060/125] feat:add cjk fulltext search --- btreeidx.cc | 5 +- ftshelpers.cc | 152 +++++++++++++++++++++++++++------------------- fulltextsearch.cc | 20 +++--- 3 files changed, 105 insertions(+), 72 deletions(-) diff --git a/btreeidx.cc b/btreeidx.cc index 0ce035cb..3f55f051 100644 --- a/btreeidx.cc +++ b/btreeidx.cc @@ -1399,6 +1399,7 @@ void BtreeIndex::getHeadwordsFromOffsets( QList & offsets, for( unsigned i = 0; i < result.size(); i++ ) { uint32_t articleOffset = result.at(i).articleOffset; + QList::Iterator it = std::lower_bound( begOffsets, endOffsets, articleOffset ); @@ -1407,7 +1408,9 @@ void BtreeIndex::getHeadwordsFromOffsets( QList & offsets, if( isCancelled && Utils::AtomicInt::loadAcquire( *isCancelled ) ) return; - headwords.append( QString::fromUtf8( ( result[ i ].prefix + result[ i ].word ).c_str() ) ); + auto word = QString::fromUtf8( ( result[ i ].prefix + result[ i ].word ).c_str() ); + + headwords.append( word ); offsets.erase( it); begOffsets = offsets.begin(); endOffsets = offsets.end(); diff --git a/ftshelpers.cc b/ftshelpers.cc index 1b7942cb..30517ae5 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -39,16 +39,26 @@ bool ftsIndexIsOldOrBad( string const & indexFile, static QString makeHiliteRegExpString( QStringList const & words, int searchMode, - int distanceBetweenWords ) + int distanceBetweenWords, bool hasCJK = false ) { QString searchString( "(" ); QString stripWords( "(?:\\W+\\w+){0," ); if( distanceBetweenWords >= 0 ) stripWords += QString::number( distanceBetweenWords ); - stripWords += "}\\W+"; + stripWords += "}"; + + if(!hasCJK) + { + stripWords += "\\W+"; + } QString boundWord( searchMode == FTS::WholeWords ? "\\b" : "(?:\\w*)"); + if(hasCJK) + { + //no boundary for CJK + boundWord.clear(); + } for( int x = 0; x < words.size(); x++ ) { @@ -62,6 +72,54 @@ static QString makeHiliteRegExpString( QStringList const & words, return searchString; } +void tokenizeCJK( QStringList & indexWords, QRegularExpression wordRegExp, QStringList list ) +{ + QStringList wordList, hieroglyphList; + for( int i = 0; i < list.size(); i ++ ) + { + QString word = list.at( i ); + + // Check for CJK symbols in word + bool parsed = false; + QString hieroglyph; + for( int x = 0; x < word.size(); x++ ) + if( isCJKChar( word.at( x ).unicode() ) ) + { + parsed = true; + hieroglyph.append( word[ x ] ); + + if( QChar( word.at( x ) ).isHighSurrogate() + && QChar( word[ x + 1 ] ).isLowSurrogate() ) + hieroglyph.append( word[ ++x ] ); + + hieroglyphList.append( hieroglyph ); + hieroglyph.clear(); + } + + // If word don't contains CJK symbols put it in list as is + if( !parsed ) + wordList.append( word ); + } + + indexWords = wordList.filter( wordRegExp ); + indexWords.removeDuplicates(); + + hieroglyphList.removeDuplicates(); + indexWords += hieroglyphList; +} + +bool containCJK( QString const & str) +{ + bool hasCJK = false; + for( int x = 0; x < str.size(); x++ ) + if( isCJKChar( str.at( x ).unicode() ) ) + { + hasCJK = true; + break; + } + return hasCJK; +} + bool parseSearchString( QString const & str, QStringList & indexWords, QStringList & searchWords, QRegExp & searchRegExp, int searchMode, @@ -76,38 +134,35 @@ bool parseSearchString( QString const & str, QStringList & indexWords, QRegularExpression setsRegExp( "\\[[^\\]]+\\]", QRegularExpression::CaseInsensitiveOption ); QRegularExpression regexRegExp( "\\\\[afnrtvdDwWsSbB]|\\\\x([0-9A-Fa-f]{4})|\\\\0([0-7]{3})", QRegularExpression::CaseInsensitiveOption); - hasCJK = false; - for( int x = 0; x < str.size(); x++ ) - if( isCJKChar( str.at( x ).unicode() ) ) - { - hasCJK = true; - break; - } + hasCJK = containCJK( str ); if( searchMode == FTS::WholeWords || searchMode == FTS::PlainText ) { - if( hasCJK ) - return false; - // Make words list for search in article text - searchWords = str.normalized( QString::NormalizationForm_C ) - .split( spacesRegExp, Qt::SkipEmptyParts ); - + searchWords = str.normalized( QString::NormalizationForm_C ).split( spacesRegExp, Qt::SkipEmptyParts ); // Make words list for index search - QStringList list = str.normalized( QString::NormalizationForm_C ) - .toLower().split( spacesRegExp, Qt::SkipEmptyParts ); - indexWords = list.filter( wordRegExp ); - indexWords.removeDuplicates(); + QStringList list = + str.normalized( QString::NormalizationForm_C ).toLower().split( spacesRegExp, Qt::SkipEmptyParts ); - // Make regexp for results hilite + QString searchString; + if( hasCJK ) + { + tokenizeCJK( indexWords, wordRegExp, list ); + // QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); + searchString = makeHiliteRegExpString( indexWords, searchMode, distanceBetweenWords, hasCJK ); + } + else + { + indexWords = list.filter( wordRegExp ); + indexWords.removeDuplicates(); - QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); - QString searchString = makeHiliteRegExpString( allWords, searchMode, distanceBetweenWords ); + // Make regexp for results hilite - searchRegExp = QRegExp( searchString, matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive, - QRegExp::RegExp2 ); + QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); + searchString = makeHiliteRegExpString( allWords, searchMode, distanceBetweenWords ); + } + searchRegExp = QRegExp( searchString, matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp2 ); searchRegExp.setMinimal( true ); - return !indexWords.isEmpty(); } else @@ -128,38 +183,7 @@ bool parseSearchString( QString const & str, QStringList & indexWords, if( hasCJK ) { - QStringList wordList, hieroglyphList; - for( int i = 0; i < list.size(); i ++ ) - { - QString word = list.at( i ); - - // Check for CJK symbols in word - bool parsed = false; - QString hieroglyph; - for( int x = 0; x < word.size(); x++ ) - if( isCJKChar( word.at( x ).unicode() ) ) - { - parsed = true; - hieroglyph.append( word[ x ] ); - - if( QChar( word.at( x ) ).isHighSurrogate() - && QChar( word[ x + 1 ] ).isLowSurrogate() ) - hieroglyph.append( word[ ++x ] ); - - hieroglyphList.append( hieroglyph ); - hieroglyph.clear(); - } - - // If word don't contains CJK symbols put it in list as is - if( !parsed ) - wordList.append( word ); - } - - indexWords = wordList.filter( wordRegExp ); - indexWords.removeDuplicates(); - - hieroglyphList.removeDuplicates(); - indexWords += hieroglyphList; + tokenizeCJK( indexWords, wordRegExp, list ); } else { @@ -543,6 +567,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, int n; for( n = 0; n < parsedWords.size(); n++ ) { + auto parsed_word = parsedWords.at( n ); if( ignoreWordsOrder ) { int i; @@ -550,8 +575,8 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, { if( wordsList.at( i ).second ) { - if( ( searchMode == FTS::WholeWords && parsedWords.at( n ).compare( wordsList.at( i ).first, cs ) == 0 ) - || ( searchMode == FTS::PlainText && parsedWords.at( n ).contains( wordsList.at( i ).first, cs ) ) ) + if( ( searchMode == FTS::WholeWords && parsed_word.compare( wordsList.at( i ).first, cs ) == 0 ) + || ( searchMode == FTS::PlainText && parsed_word.contains( wordsList.at( i ).first, cs ) ) ) { wordsList[ i ].second = false; @@ -630,8 +655,13 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, } else { - if( ( searchMode == FTS::WholeWords && parsedWords.at( n ).compare( words.at( matchWordNom ), cs ) == 0 ) - || ( searchMode == FTS::PlainText && parsedWords.at( n ).contains( words.at( matchWordNom ), cs ) ) ) + //for cjk word, FTS::WholeWords and FTS::PlainText actually have same effect. + auto match_word = words.at( matchWordNom ); + bool hasCJK = containCJK( match_word ); + if( ( searchMode == FTS::WholeWords && + ( ( !hasCJK&& parsed_word.compare( match_word, cs ) == 0 ) || + ( hasCJK && parsed_word.contains( match_word, cs ) ) ) ) + || ( searchMode == FTS::PlainText && parsed_word.contains( match_word, cs ) ) ) { matchWordNom += 1; diff --git a/fulltextsearch.cc b/fulltextsearch.cc index 57842817..749a2a94 100644 --- a/fulltextsearch.cc +++ b/fulltextsearch.cc @@ -353,16 +353,16 @@ void FullTextSearchDialog::accept() distanceBetweenWords, hasCJK ) ) { - if( hasCJK && ( mode == WholeWords || mode == PlainText ) ) - { - QMessageBox message( QMessageBox::Warning, - "GoldenDict", - tr( "CJK symbols in search string are not compatible with search modes \"Whole words\" and \"Plain text\"" ), - QMessageBox::Ok, - this ); - message.exec(); - } - else +// if( hasCJK && ( mode == WholeWords || mode == PlainText ) ) +// { +// QMessageBox message( QMessageBox::Warning, +// "GoldenDict", +// tr( "CJK symbols in search string are not compatible with search modes \"Whole words\" and \"Plain text\"" ), +// QMessageBox::Ok, +// this ); +// message.exec(); +// } +// else { QMessageBox message( QMessageBox::Warning, "GoldenDict", From 5fc3bd8d833b31df9e39e9e3881509af9e8ae52c Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 20:47:42 +0800 Subject: [PATCH 061/125] modify clang format --- .clang-format | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/.clang-format b/.clang-format index 5345366d..af33ee77 100644 --- a/.clang-format +++ b/.clang-format @@ -1,28 +1,30 @@ +# Format Style Options - Created with Clang Power Tools --- -BasedOnStyle: LLVM AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: AcrossComments AlignEscapedNewlines: Left -AllowAllArgumentsOnNextLine: 'false' -AllowShortBlocksOnASingleLine: 'false' +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: Never -BinPackArguments: 'false' -BinPackParameters: 'false' +BasedOnStyle: LLVM +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Allman BreakConstructorInitializers: AfterColon -ColumnLimit: '120' -ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' -ConstructorInitializerIndentWidth: '2' -ContinuationIndentWidth: '2' -MaxEmptyLinesToKeep: '1' +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth : 2 +ContinuationIndentWidth: 2 +MaxEmptyLinesToKeep: 1 PointerAlignment: Middle -SortIncludes: 'false' -SortUsingDeclarations: 'false' +SortIncludes: false +SortUsingDeclarations: false SpaceBeforeParens: Never -SpacesInAngles: 'true' -SpacesInParentheses: 'true' -SpacesInSquareBrackets: 'true' +SpacesInAngles: true +SpacesInParentheses: true +SpacesInSquareBrackets: true UseTab: Never -AlignConsecutiveAssignments: AcrossComments - ... From ba10f68c1c4cb0e523f921ebe21520b66982aa68 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 21:28:41 +0800 Subject: [PATCH 062/125] clean code: replace throw() with noexcept --- aard.cc | 12 ++++++------ bgl.cc | 12 ++++++------ btreeidx.hh | 2 +- chinese.cc | 4 ++-- config.cc | 20 ++++++++++---------- config.hh | 20 ++++++++++---------- dictdfiles.cc | 12 ++++++------ dictionary.cc | 12 ++++++------ dictionary.hh | 28 ++++++++++++++-------------- dictserver.cc | 12 ++++++------ dsl.cc | 12 ++++++------ dsl_details.cc | 2 +- dsl_details.hh | 2 +- epwing.cc | 12 ++++++------ ex.hh | 8 ++++---- file.cc | 4 ++-- file.hh | 6 +++--- forvo.cc | 12 ++++++------ gls.cc | 16 ++++++++-------- hunspell.cc | 16 ++++++++-------- loaddictionaries.cc | 2 +- loaddictionaries.hh | 2 +- lsa.cc | 14 +++++++------- mdx.cc | 12 ++++++------ mediawiki.cc | 12 ++++++------ programs.cc | 12 ++++++------ sdict.cc | 12 ++++++------ slob.cc | 12 ++++++------ sounddir.cc | 12 ++++++------ stardict.cc | 12 ++++++------ transliteration.cc | 10 +++++----- transliteration.hh | 12 ++++++------ utf8.cc | 2 +- utf8.hh | 2 +- voiceengines.cc | 12 ++++++------ website.cc | 12 ++++++------ xdxf.cc | 12 ++++++------ zim.cc | 12 ++++++------ zipsounds.cc | 14 +++++++------- 39 files changed, 207 insertions(+), 207 deletions(-) diff --git a/aard.cc b/aard.cc index 5fe9e694..d6a4eab7 100644 --- a/aard.cc +++ b/aard.cc @@ -239,16 +239,16 @@ class AardDictionary: public BtreeIndexing::BtreeDictionary ~AardDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -284,7 +284,7 @@ class AardDictionary: public BtreeIndexing::BtreeDictionary protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -338,7 +338,7 @@ AardDictionary::~AardDictionary() df.close(); } -void AardDictionary::loadIcon() throw() +void AardDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/bgl.cc b/bgl.cc index 569c2916..6d692dbd 100644 --- a/bgl.cc +++ b/bgl.cc @@ -198,16 +198,16 @@ namespace BglDictionary( string const & id, string const & indexFile, string const & dictionaryFile ); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -249,7 +249,7 @@ namespace protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -302,7 +302,7 @@ namespace FTS_index_completed.ref(); } - void BglDictionary::loadIcon() throw() + void BglDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/btreeidx.hh b/btreeidx.hh index deb3a6b6..a98e4841 100644 --- a/btreeidx.hh +++ b/btreeidx.hh @@ -155,7 +155,7 @@ public: BtreeDictionary( string const & id, vector< string > const & dictionaryFiles ); /// Btree-indexed dictionaries are usually a good source for compound searches. - virtual Dictionary::Features getFeatures() const throw() + virtual Dictionary::Features getFeatures() const noexcept { return Dictionary::SuitableForCompoundSearching; } /// This function does the search using the btree index. Derivatives usually diff --git a/chinese.cc b/chinese.cc index 400a0115..35a092f8 100644 --- a/chinese.cc +++ b/chinese.cc @@ -31,7 +31,7 @@ public: ~CharacterConversionDictionary(); std::vector< gd::wstring > getAlternateWritings( gd::wstring const & ) - throw(); + noexcept; }; CharacterConversionDictionary::CharacterConversionDictionary( std::string const & id, @@ -70,7 +70,7 @@ CharacterConversionDictionary::~CharacterConversionDictionary() } std::vector< gd::wstring > CharacterConversionDictionary::getAlternateWritings( gd::wstring const & str ) - throw() + noexcept { std::vector< gd::wstring > results; diff --git a/config.cc b/config.cc index f4311f55..a86e8c7c 100644 --- a/config.cc +++ b/config.cc @@ -2252,7 +2252,7 @@ QString getUserQtCssFileName() return getHomeDir().filePath( "qt-style.css" ); } -QString getProgramDataDir() throw() +QString getProgramDataDir() noexcept { if ( isPortableVersion() ) return QCoreApplication::applicationDirPath(); @@ -2264,12 +2264,12 @@ QString getProgramDataDir() throw() #endif } -QString getEmbedLocDir() throw() +QString getEmbedLocDir() noexcept { return ":/locale"; } -QString getLocDir() throw() +QString getLocDir() noexcept { if ( QDir( getProgramDataDir() ).cd( "locale" ) ) return getProgramDataDir() + "/locale"; @@ -2277,7 +2277,7 @@ QString getLocDir() throw() return QCoreApplication::applicationDirPath() + "/locale"; } -QString getHelpDir() throw() +QString getHelpDir() noexcept { if ( QDir( getProgramDataDir() ).cd( "help" ) ) return getProgramDataDir() + "/help"; @@ -2286,7 +2286,7 @@ QString getHelpDir() throw() } #ifdef MAKE_CHINESE_CONVERSION_SUPPORT -QString getOpenCCDir() throw() +QString getOpenCCDir() noexcept { #if defined( Q_OS_WIN ) if ( QDir( "opencc" ).exists() ) @@ -2305,7 +2305,7 @@ QString getOpenCCDir() throw() } #endif -bool isPortableVersion() throw() +bool isPortableVersion() noexcept { struct IsPortable { @@ -2320,7 +2320,7 @@ bool isPortableVersion() throw() return p.isPortable; } -QString getPortableVersionDictionaryDir() throw() +QString getPortableVersionDictionaryDir() noexcept { if ( isPortableVersion() ) return getProgramDataDir() + "/content"; @@ -2328,7 +2328,7 @@ QString getPortableVersionDictionaryDir() throw() return QString(); } -QString getPortableVersionMorphoDir() throw() +QString getPortableVersionMorphoDir() noexcept { if ( isPortableVersion() ) return getPortableVersionDictionaryDir() + "/morphology"; @@ -2348,7 +2348,7 @@ QString getStylesDir() return result.path() + QDir::separator(); } -QString getCacheDir() throw() +QString getCacheDir() noexcept { return isPortableVersion() ? portableHomeDirPath() + "/cache" #ifdef HAVE_X11 @@ -2358,7 +2358,7 @@ QString getCacheDir() throw() #endif } -QString getNetworkCacheDir() throw() +QString getNetworkCacheDir() noexcept { return getCacheDir() + "/network"; } diff --git a/config.hh b/config.hh index 9d1a0ae9..06d21ccf 100644 --- a/config.hh +++ b/config.hh @@ -808,40 +808,40 @@ QString getUserQtCssFileName() ; /// Returns the program's data dir. Under Linux that would be something like /// /usr/share/apps/goldendict, under Windows C:/Program Files/GoldenDict. -QString getProgramDataDir() throw(); +QString getProgramDataDir() noexcept; /// Returns the directory storing program localizized files (.qm). -QString getEmbedLocDir() throw(); -QString getLocDir() throw(); +QString getEmbedLocDir() noexcept; +QString getLocDir() noexcept; /// Returns the directory storing program help files (.qch). -QString getHelpDir() throw(); +QString getHelpDir() noexcept; #ifdef MAKE_CHINESE_CONVERSION_SUPPORT /// Returns the directory storing OpenCC configuration and dictionary files (.json and .ocd). -QString getOpenCCDir() throw(); +QString getOpenCCDir() noexcept; #endif /// Returns true if the program is configured as a portable version. In that /// mode, all the settings and indices are kept in the program's directory. -bool isPortableVersion() throw(); +bool isPortableVersion() noexcept; /// Returns directory with dictionaries for portable version. It is content/ /// in the application's directory. -QString getPortableVersionDictionaryDir() throw(); +QString getPortableVersionDictionaryDir() noexcept; /// Returns directory with morpgologies for portable version. It is /// content/morphology in the application's directory. -QString getPortableVersionMorphoDir() throw(); +QString getPortableVersionMorphoDir() noexcept; /// Returns the add-on styles directory. QString getStylesDir(); /// Returns the directory where user-specific non-essential (cached) data should be written. -QString getCacheDir() throw(); +QString getCacheDir() noexcept; /// Returns the article network disk cache directory. -QString getNetworkCacheDir() throw(); +QString getNetworkCacheDir() noexcept; } diff --git a/dictdfiles.cc b/dictdfiles.cc index f8607269..ab3fa777 100644 --- a/dictdfiles.cc +++ b/dictdfiles.cc @@ -100,19 +100,19 @@ public: ~DictdDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; inline virtual quint32 getLangFrom() const { return idxHeader.langFrom; } @@ -215,7 +215,7 @@ string nameFromFileName( string const & indexFileName ) return Utf8::encode( FsEncoding::decode( string( sep + 1, dot - sep - 1 ) ) ); } -void DictdDictionary::loadIcon() throw() +void DictdDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/dictionary.cc b/dictionary.cc index 7ccc36e2..11b13c29 100644 --- a/dictionary.cc +++ b/dictionary.cc @@ -159,7 +159,7 @@ sptr< WordSearchRequest > Class::findHeadwordsForSynonym( wstring const & ) } vector< wstring > Class::getAlternateWritings( wstring const & ) - throw() + noexcept { return vector< wstring >(); } @@ -185,21 +185,21 @@ QString Class::getMainFilename() return QString(); } -QIcon const & Class::getIcon() throw() +QIcon const & Class::getIcon() noexcept { if( !dictionaryIconLoaded ) loadIcon(); return dictionaryIcon; } -QIcon const & Class::getNativeIcon() throw() +QIcon const & Class::getNativeIcon() noexcept { if( !dictionaryIconLoaded ) loadIcon(); return dictionaryNativeIcon; } -void Class::loadIcon() throw() +void Class::loadIcon() noexcept { dictionaryIconLoaded = true; } @@ -424,7 +424,7 @@ void Class::isolateCSS( QString & css, QString const & wrapperSelector ) css = newCSS; } -string makeDictionaryId( vector< string > const & dictionaryFiles ) throw() +string makeDictionaryId( vector< string > const & dictionaryFiles ) noexcept { std::vector< string > sortedList; @@ -470,7 +470,7 @@ string makeDictionaryId( vector< string > const & dictionaryFiles ) throw() // of a timestamp of the file, so we use here Qt anyway. It is supposed to // be fixed in the future when it's needed. bool needToRebuildIndex( vector< string > const & dictionaryFiles, - string const & indexFile ) throw() + string const & indexFile ) noexcept { unsigned long lastModified = 0; diff --git a/dictionary.hh b/dictionary.hh index b21933e2..08b9ff5a 100644 --- a/dictionary.hh +++ b/dictionary.hh @@ -270,7 +270,7 @@ protected: // Load user icon if it exist // By default set icon to empty - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; // Load icon from filename directly if isFullName == true // else treat filename as name without extension @@ -295,39 +295,39 @@ public: virtual void deferredInit(); /// Returns the dictionary's id. - string getId() throw() + string getId() noexcept { return id; } /// Returns the list of file names the dictionary consists of. - vector< string > const & getDictionaryFilenames() throw() + vector< string > const & getDictionaryFilenames() noexcept { return dictionaryFiles; } /// Returns the dictionary's full name, utf8. - virtual string getName() throw()=0; + virtual string getName() noexcept=0; /// Returns all the available properties, like the author's name, copyright, /// description etc. All strings are in utf8. - virtual map< Property, string > getProperties() throw()=0; + virtual map< Property, string > getProperties() noexcept=0; /// Returns the features the dictionary possess. See the Feature enum for /// their list. - virtual Features getFeatures() const throw() + virtual Features getFeatures() const noexcept { return NoFeatures; } /// Returns the number of articles in the dictionary. - virtual unsigned long getArticleCount() throw()=0; + virtual unsigned long getArticleCount() noexcept=0; /// Returns the number of words in the dictionary. This can be equal to /// the number of articles, or can be larger if some synonyms are present. - virtual unsigned long getWordCount() throw()=0; + virtual unsigned long getWordCount() noexcept=0; /// Returns the dictionary's icon. - virtual QIcon const & getIcon() throw(); + virtual QIcon const & getIcon() noexcept; /// Returns the dictionary's native icon. Dsl icons are usually rectangular, /// and are adapted by getIcon() to be square. This function allows getting /// the original icon with no geometry transformations applied. - virtual QIcon const & getNativeIcon() throw(); + virtual QIcon const & getNativeIcon() noexcept; /// Returns the dictionary's source language. virtual quint32 getLangFrom() const @@ -371,7 +371,7 @@ public: /// supposed to be very fast and simple, and the results are thus returned /// synchronously. virtual vector< wstring > getAlternateWritings( wstring const & ) - throw(); + noexcept; /// Returns a definition for the given word. The definition should /// be an html fragment (without html/head/body tags) in an utf8 encoding. @@ -454,7 +454,7 @@ public: /// dictionary is being indexed. Since indexing can take some time, this /// is useful to show in some kind of a splash screen. /// The dictionaryName is in utf8. - virtual void indexingDictionary( string const & dictionaryName ) throw()=0; + virtual void indexingDictionary( string const & dictionaryName ) noexcept=0; virtual ~Initializing() {} @@ -465,7 +465,7 @@ public: /// hashing the file names. This id should be used to identify dictionary /// and for the index file name, if one is needed. /// This function is supposed to be used by dictionary implementations. -string makeDictionaryId( vector< string > const & dictionaryFiles ) throw(); +string makeDictionaryId( vector< string > const & dictionaryFiles ) noexcept; /// Checks if it is needed to regenerate index file based on its timestamp /// and the timestamps of the dictionary files. If some files are newer than @@ -473,7 +473,7 @@ string makeDictionaryId( vector< string > const & dictionaryFiles ) throw(); /// dictionary files don't exist, returns true, too. /// This function is supposed to be used by dictionary implementations. bool needToRebuildIndex( vector< string > const & dictionaryFiles, - string const & indexFile ) throw(); + string const & indexFile ) noexcept; /// Returns a random dictionary id useful for interactively created /// dictionaries. diff --git a/dictserver.cc b/dictserver.cc index 65761655..70f3424b 100644 --- a/dictserver.cc +++ b/dictserver.cc @@ -209,16 +209,16 @@ public: strategies.append( "prefix" ); } - virtual string getName() throw() + virtual string getName() noexcept { return name; } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const &, @@ -237,7 +237,7 @@ public: virtual QString const & getDescription(); protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; void getServerDatabases(); @@ -245,7 +245,7 @@ protected: friend class DictServerArticleRequest; }; -void DictServerDictionary::loadIcon() throw() +void DictServerDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/dsl.cc b/dsl.cc index ae1e6b42..84bd687e 100644 --- a/dsl.cc +++ b/dsl.cc @@ -183,16 +183,16 @@ public: ~DslDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -247,7 +247,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -466,7 +466,7 @@ void DslDictionary::doDeferredInit() } -void DslDictionary::loadIcon() throw() +void DslDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/dsl_details.cc b/dsl_details.cc index 5accaf57..465309ac 100644 --- a/dsl_details.cc +++ b/dsl_details.cc @@ -983,7 +983,7 @@ DslScanner::DslScanner( string const & fileName ) : readBufferLeft = 0; } -DslScanner::~DslScanner() throw() +DslScanner::~DslScanner() noexcept { gzclose( f ); } diff --git a/dsl_details.hh b/dsl_details.hh index 2349c349..1044a919 100644 --- a/dsl_details.hh +++ b/dsl_details.hh @@ -130,7 +130,7 @@ public: DEF_EX( exEncodingError, "Encoding error", Ex ) // Should never happen really DslScanner( string const & fileName ) ; - ~DslScanner() throw(); + ~DslScanner() noexcept; /// Returns the detected encoding of this file. Encoding getEncoding() const diff --git a/epwing.cc b/epwing.cc index 637e0b6c..2f9ef607 100644 --- a/epwing.cc +++ b/epwing.cc @@ -96,16 +96,16 @@ public: ~EpwingDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return bookName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -163,7 +163,7 @@ public: protected: - void loadIcon() throw(); + void loadIcon() noexcept; private: @@ -244,7 +244,7 @@ EpwingDictionary::~EpwingDictionary() removeDirectory( cacheDirectory ); } -void EpwingDictionary::loadIcon() throw() +void EpwingDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/ex.hh b/ex.hh index cc76846a..13dd5fff 100644 --- a/ex.hh +++ b/ex.hh @@ -14,8 +14,8 @@ #define DEF_EX( exName, exDescription, exParent ) \ class exName: public exParent { \ public: \ -virtual const char * what() const throw() { return (exDescription); } \ -virtual ~exName() throw() {} }; +virtual const char * what() const noexcept { return (exDescription); } \ +virtual ~exName() noexcept {} }; /// Same as DEF_EX, but takes a runtime string argument, which gets concatenated /// with the description. @@ -31,7 +31,7 @@ class exName: public exParent { \ std::string value; \ public: \ exName( std::string const & value_ ): value( std::string( exDescription ) + " " + value_ ) {} \ -virtual const char * what() const throw() { return value.c_str(); } \ -virtual ~exName() throw() {} }; +virtual const char * what() const noexcept { return value.c_str(); } \ +virtual ~exName() noexcept {} }; #endif diff --git a/file.cc b/file.cc index 9391028a..6050eef3 100644 --- a/file.cc +++ b/file.cc @@ -65,7 +65,7 @@ void loadFromFile( std::string const & n, std::vector< char > & data ) f.read( &data.front(), data.size() ); } -bool exists( char const * filename ) throw() +bool exists( char const * filename ) noexcept { #ifdef __WIN32 #if defined(__WIN64) || defined(_MSC_VER) @@ -313,7 +313,7 @@ void Class::close() f.close(); } -Class::~Class() throw() +Class::~Class() noexcept { if ( f.isOpen() ) { diff --git a/file.hh b/file.hh index 81281647..c4c1c50d 100644 --- a/file.hh +++ b/file.hh @@ -30,9 +30,9 @@ bool tryPossibleZipName( std::string const & name, std::string & copyTo ); void loadFromFile( std::string const & n, std::vector< char > & data ); -bool exists( char const * filename ) throw(); +bool exists( char const * filename ) noexcept; -inline bool exists( std::string const & filename ) throw() +inline bool exists( std::string const & filename ) noexcept { return exists( filename.c_str() ); } class Class @@ -116,7 +116,7 @@ public: /// Closes the file. No further operations are valid. void close() ; - ~Class() throw(); + ~Class() noexcept; private: diff --git a/forvo.cc b/forvo.cc index a1d63f8d..cdfeecc5 100644 --- a/forvo.cc +++ b/forvo.cc @@ -42,16 +42,16 @@ public: { } - virtual string getName() throw() + virtual string getName() noexcept { return name; } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const & /*word*/, @@ -70,7 +70,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; @@ -90,7 +90,7 @@ sptr< DataRequest > ForvoDictionary::getArticle( wstring const & word, netMgr ); } -void ForvoDictionary::loadIcon() throw() +void ForvoDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/gls.cc b/gls.cc index 30d8cf23..48c5a0c5 100644 --- a/gls.cc +++ b/gls.cc @@ -88,7 +88,7 @@ public: DEF_EX( exEncodingError, "Encoding error", Ex ) // Should never happen really GlsScanner( string const & fileName ) ; - ~GlsScanner() throw(); + ~GlsScanner() noexcept; /// Returns the detected encoding of this file. Encoding getEncoding() const @@ -293,7 +293,7 @@ bool GlsScanner::readNextLine( wstring & out, size_t & offset ) } } -GlsScanner::~GlsScanner() throw() +GlsScanner::~GlsScanner() noexcept { gzclose( f ); } @@ -372,16 +372,16 @@ public: ~GlsDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -425,7 +425,7 @@ public: } protected: - void loadIcon() throw(); + void loadIcon() noexcept; private: @@ -517,7 +517,7 @@ GlsDictionary::~GlsDictionary() dict_data_close( dz ); } -void GlsDictionary::loadIcon() throw() +void GlsDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/hunspell.cc b/hunspell.cc index c09638c3..70cb9d45 100644 --- a/hunspell.cc +++ b/hunspell.cc @@ -63,16 +63,16 @@ public: { } - virtual string getName() throw() + virtual string getName() noexcept { return name; } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const &, @@ -91,11 +91,11 @@ public: virtual bool isLocalDictionary() { return true; } - virtual vector< wstring > getAlternateWritings( const wstring & word ) throw(); + virtual vector< wstring > getAlternateWritings( const wstring & word ) noexcept; protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -142,7 +142,7 @@ bool containsWhitespace( wstring const & str ) return false; } -void HunspellDictionary::loadIcon() throw() +void HunspellDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; @@ -162,7 +162,7 @@ void HunspellDictionary::loadIcon() throw() dictionaryIconLoaded = true; } -vector< wstring > HunspellDictionary::getAlternateWritings( wstring const & word ) throw() +vector< wstring > HunspellDictionary::getAlternateWritings( wstring const & word ) noexcept { vector< wstring > results; diff --git a/loaddictionaries.cc b/loaddictionaries.cc index 24bdd1aa..38c88a1f 100644 --- a/loaddictionaries.cc +++ b/loaddictionaries.cc @@ -240,7 +240,7 @@ void LoadDictionaries::handlePath( Config::Path const & path ) #endif } -void LoadDictionaries::indexingDictionary( string const & dictionaryName ) throw() +void LoadDictionaries::indexingDictionary( string const & dictionaryName ) noexcept { emit indexingDictionarySignal( QString::fromUtf8( dictionaryName.c_str() ) ); } diff --git a/loaddictionaries.hh b/loaddictionaries.hh index 2c31f509..d9960c6a 100644 --- a/loaddictionaries.hh +++ b/loaddictionaries.hh @@ -46,7 +46,7 @@ signals: public: - virtual void indexingDictionary( std::string const & dictionaryName ) throw(); + virtual void indexingDictionary( std::string const & dictionaryName ) noexcept; private: diff --git a/lsa.cc b/lsa.cc index a6944a43..f30d1d6c 100644 --- a/lsa.cc +++ b/lsa.cc @@ -164,15 +164,15 @@ public: LsaDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ); - virtual string getName() throw(); + virtual string getName() noexcept; - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.soundsCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return getArticleCount(); } virtual sptr< Dictionary::DataRequest > getArticle( wstring const &, @@ -186,10 +186,10 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; -string LsaDictionary::getName() throw() +string LsaDictionary::getName() noexcept { string result = FsEncoding::basename( getDictionaryFilenames()[ 0 ] ); @@ -498,7 +498,7 @@ sptr< Dictionary::DataRequest > LsaDictionary::getResource( string const & name return dr; } -void LsaDictionary::loadIcon() throw() +void LsaDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/mdx.cc b/mdx.cc index 757de69f..4165d0ab 100644 --- a/mdx.cc +++ b/mdx.cc @@ -266,22 +266,22 @@ public: virtual void deferredInit(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } @@ -327,7 +327,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -925,7 +925,7 @@ const QString & MdxDictionary::getDescription() return dictionaryDescription; } -void MdxDictionary::loadIcon() throw() +void MdxDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/mediawiki.cc b/mediawiki.cc index e0a62325..96998517 100644 --- a/mediawiki.cc +++ b/mediawiki.cc @@ -47,16 +47,16 @@ public: langId = LangCoder::code2toInt( url.mid( n - 2, 2 ).toLatin1().data() ); } - virtual string getName() throw() + virtual string getName() noexcept { return name; } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const &, @@ -73,11 +73,11 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; -void MediaWikiDictionary::loadIcon() throw() +void MediaWikiDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/programs.cc b/programs.cc index cd2b07df..f3a619ca 100644 --- a/programs.cc +++ b/programs.cc @@ -30,16 +30,16 @@ public: { } - virtual string getName() throw() + virtual string getName() noexcept { return prg.name.toUtf8().data(); } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const & word, @@ -53,7 +53,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; sptr< WordSearchRequest > ProgramsDictionary::prefixMatch( wstring const & word, @@ -118,7 +118,7 @@ sptr< Dictionary::DataRequest > ProgramsDictionary::getArticle( } } -void ProgramsDictionary::loadIcon() throw() +void ProgramsDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/sdict.cc b/sdict.cc index 0b1cb8a5..3162d838 100644 --- a/sdict.cc +++ b/sdict.cc @@ -143,16 +143,16 @@ class SdictDictionary: public BtreeIndexing::BtreeDictionary ~SdictDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -187,7 +187,7 @@ class SdictDictionary: public BtreeIndexing::BtreeDictionary } protected: - void loadIcon() throw(); + void loadIcon() noexcept; private: @@ -240,7 +240,7 @@ SdictDictionary::~SdictDictionary() df.close(); } -void SdictDictionary::loadIcon() throw() +void SdictDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/slob.cc b/slob.cc index b66e061c..7851ebec 100644 --- a/slob.cc +++ b/slob.cc @@ -587,16 +587,16 @@ class SlobDictionary: public BtreeIndexing::BtreeDictionary ~SlobDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -643,7 +643,7 @@ class SlobDictionary: public BtreeIndexing::BtreeDictionary protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -746,7 +746,7 @@ void SlobDictionary::removeDirectory( QString const & directory ) dir.rmdir( directory ); } -void SlobDictionary::loadIcon() throw() +void SlobDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/sounddir.cc b/sounddir.cc index 46b58749..095b3abf 100644 --- a/sounddir.cc +++ b/sounddir.cc @@ -79,16 +79,16 @@ public: vector< string > const & dictionaryFiles, QString const & iconFilename_ ); - virtual string getName() throw() + virtual string getName() noexcept { return name; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.soundsCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return getArticleCount(); } virtual sptr< Dictionary::DataRequest > getArticle( wstring const &, @@ -102,7 +102,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; SoundDirDictionary::SoundDirDictionary( string const & id, @@ -289,7 +289,7 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getArticle( wstring const & return ret; } -void SoundDirDictionary::loadIcon() throw() +void SoundDirDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/stardict.cc b/stardict.cc index 9c3f347e..34417f29 100644 --- a/stardict.cc +++ b/stardict.cc @@ -155,16 +155,16 @@ public: ~StardictDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return bookName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.wordCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount + idxHeader.synWordCount; } inline virtual quint32 getLangFrom() const @@ -207,7 +207,7 @@ public: } protected: - void loadIcon() throw(); + void loadIcon() noexcept; private: @@ -292,7 +292,7 @@ StardictDictionary::~StardictDictionary() dict_data_close( dz ); } -void StardictDictionary::loadIcon() throw() +void StardictDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/transliteration.cc b/transliteration.cc index aefc13fd..fc8604ae 100644 --- a/transliteration.cc +++ b/transliteration.cc @@ -22,16 +22,16 @@ BaseTransliterationDictionary::BaseTransliterationDictionary( string const & id, dictionaryIconLoaded = true; } -string BaseTransliterationDictionary::getName() throw() +string BaseTransliterationDictionary::getName() noexcept { return name; } -map< Dictionary::Property, string > BaseTransliterationDictionary::getProperties() throw() +map< Dictionary::Property, string > BaseTransliterationDictionary::getProperties() noexcept { return map< Dictionary::Property, string >(); } -unsigned long BaseTransliterationDictionary::getArticleCount() throw() +unsigned long BaseTransliterationDictionary::getArticleCount() noexcept { return 0; } -unsigned long BaseTransliterationDictionary::getWordCount() throw() +unsigned long BaseTransliterationDictionary::getWordCount() noexcept { return 0; } sptr< Dictionary::WordSearchRequest > BaseTransliterationDictionary::prefixMatch( wstring const &, @@ -83,7 +83,7 @@ TransliterationDictionary::TransliterationDictionary( string const & id, } vector< wstring > TransliterationDictionary::getAlternateWritings( wstring const & str ) - throw() + noexcept { vector< wstring > results; diff --git a/transliteration.hh b/transliteration.hh index d6d96198..b35a9785 100644 --- a/transliteration.hh +++ b/transliteration.hh @@ -28,16 +28,16 @@ public: BaseTransliterationDictionary( string const & id, string const & name, QIcon icon, bool caseSensitive = true ); - virtual string getName() throw(); + virtual string getName() noexcept; - virtual map< Dictionary::Property, string > getProperties() throw(); + virtual map< Dictionary::Property, string > getProperties() noexcept; - virtual unsigned long getArticleCount() throw(); + virtual unsigned long getArticleCount() noexcept; - virtual unsigned long getWordCount() throw(); + virtual unsigned long getWordCount() noexcept; virtual vector< wstring > getAlternateWritings( wstring const & ) - throw() = 0; + noexcept = 0; virtual sptr< Dictionary::WordSearchRequest > findHeadwordsForSynonym( wstring const & ) ; @@ -85,7 +85,7 @@ public: bool caseSensitive = true ); virtual vector< wstring > getAlternateWritings( wstring const & ) - throw(); + noexcept; }; } diff --git a/utf8.cc b/utf8.cc index 91618f92..6a6949de 100644 --- a/utf8.cc +++ b/utf8.cc @@ -132,7 +132,7 @@ long decode( char const * in_, size_t inSize, wchar * out_ ) return out - out_; } -string encode( wstring const & in ) throw() +string encode( wstring const & in ) noexcept { if( in.size() == 0 ) return string(); diff --git a/utf8.hh b/utf8.hh index ce636276..75f96503 100644 --- a/utf8.hh +++ b/utf8.hh @@ -44,7 +44,7 @@ size_t encode( wchar const * in, size_t inSize, char * out ); long decode( char const * in, size_t inSize, wchar * out ); /// Versions for non time-critical code. -string encode( wstring const & ) throw(); +string encode( wstring const & ) noexcept; wstring decode( string const & ) ; /// Since the standard isspace() is locale-specific, we need something diff --git a/voiceengines.cc b/voiceengines.cc index 0582dd36..555c5532 100644 --- a/voiceengines.cc +++ b/voiceengines.cc @@ -44,16 +44,16 @@ public: { } - virtual string getName() throw() + virtual string getName() noexcept { return voiceEngine.name.toUtf8().data(); } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const & word, @@ -67,7 +67,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; sptr< WordSearchRequest > VoiceEnginesDictionary::prefixMatch( wstring const & /*word*/, @@ -110,7 +110,7 @@ sptr< Dictionary::DataRequest > VoiceEnginesDictionary::getArticle( return ret; } -void VoiceEnginesDictionary::loadIcon() throw() +void VoiceEnginesDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/website.cc b/website.cc index 6c62e19a..4091d198 100644 --- a/website.cc +++ b/website.cc @@ -53,16 +53,16 @@ public: dictionaryDescription = temp; } - virtual string getName() throw() + virtual string getName() noexcept { return name; } - virtual map< Property, string > getProperties() throw() + virtual map< Property, string > getProperties() noexcept { return map< Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return 0; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return 0; } virtual sptr< WordSearchRequest > prefixMatch( wstring const & word, @@ -79,7 +79,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; sptr< WordSearchRequest > WebSiteDictionary::prefixMatch( wstring const & /*word*/, @@ -526,7 +526,7 @@ sptr< Dictionary::DataRequest > WebSiteDictionary::getResource( string const & n return new WebSiteResourceRequest( link, netMgr, this ); } -void WebSiteDictionary::loadIcon() throw() +void WebSiteDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/xdxf.cc b/xdxf.cc index 585e8abe..e33fc55e 100644 --- a/xdxf.cc +++ b/xdxf.cc @@ -147,16 +147,16 @@ public: ~XdxfDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -200,7 +200,7 @@ public: protected: - void loadIcon() throw(); + void loadIcon() noexcept; private: @@ -312,7 +312,7 @@ XdxfDictionary::~XdxfDictionary() dict_data_close( dz ); } -void XdxfDictionary::loadIcon() throw() +void XdxfDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/zim.cc b/zim.cc index 2de86d08..e054778d 100644 --- a/zim.cc +++ b/zim.cc @@ -669,16 +669,16 @@ class ZimDictionary: public BtreeIndexing::BtreeDictionary ~ZimDictionary(); - virtual string getName() throw() + virtual string getName() noexcept { return dictionaryName; } - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.articleCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return idxHeader.wordCount; } inline virtual quint32 getLangFrom() const @@ -725,7 +725,7 @@ class ZimDictionary: public BtreeIndexing::BtreeDictionary protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; private: @@ -792,7 +792,7 @@ ZimDictionary::~ZimDictionary() df.close(); } -void ZimDictionary::loadIcon() throw() +void ZimDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; diff --git a/zipsounds.cc b/zipsounds.cc index 95c903a4..07595c14 100644 --- a/zipsounds.cc +++ b/zipsounds.cc @@ -114,15 +114,15 @@ public: ZipSoundsDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ); - virtual string getName() throw(); + virtual string getName() noexcept; - virtual map< Dictionary::Property, string > getProperties() throw() + virtual map< Dictionary::Property, string > getProperties() noexcept { return map< Dictionary::Property, string >(); } - virtual unsigned long getArticleCount() throw() + virtual unsigned long getArticleCount() noexcept { return idxHeader.soundsCount; } - virtual unsigned long getWordCount() throw() + virtual unsigned long getWordCount() noexcept { return getArticleCount(); } virtual sptr< Dictionary::DataRequest > getArticle( wstring const &, @@ -136,7 +136,7 @@ public: protected: - virtual void loadIcon() throw(); + virtual void loadIcon() noexcept; }; ZipSoundsDictionary::ZipSoundsDictionary( string const & id, @@ -164,7 +164,7 @@ ZipSoundsDictionary::ZipSoundsDictionary( string const & id, } -string ZipSoundsDictionary::getName() throw() +string ZipSoundsDictionary::getName() noexcept { string result = FsEncoding::basename( getDictionaryFilenames()[ 0 ] ); @@ -384,7 +384,7 @@ sptr< Dictionary::DataRequest > ZipSoundsDictionary::getResource( string const & return new Dictionary::DataRequestInstant( false ); } -void ZipSoundsDictionary::loadIcon() throw() +void ZipSoundsDictionary::loadIcon() noexcept { if ( dictionaryIconLoaded ) return; From 3e05e711e5ebab7ca5c4f34b30b90df225caaecf Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 3 Jun 2022 20:07:14 +0800 Subject: [PATCH 063/125] improve: fulltext search checkArticle optimize --- ftshelpers.cc | 355 ++++++++++++++-------------------------------- ftshelpers.hh | 3 +- fulltextsearch.cc | 2 +- utils.hh | 10 ++ 4 files changed, 120 insertions(+), 250 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 30517ae5..e1b51641 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -15,6 +15,7 @@ #include #include + #include "wildcard.hh" using std::vector; @@ -38,8 +39,7 @@ bool ftsIndexIsOldOrBad( string const & indexFile, } static QString makeHiliteRegExpString( QStringList const & words, - int searchMode, - int distanceBetweenWords, bool hasCJK = false ) + int searchMode, int distanceBetweenWords, bool hasCJK = false, bool ignoreWordsOrder = false ) { QString searchString( "(" ); @@ -63,9 +63,20 @@ static QString makeHiliteRegExpString( QStringList const & words, for( int x = 0; x < words.size(); x++ ) { if( x ) + { searchString += stripWords; + if(ignoreWordsOrder) + searchString += "("; + } searchString += boundWord + words[ x ] + boundWord; + + if( x ) + { + if( ignoreWordsOrder ) + searchString += ")?"; + } + } searchString += ")"; @@ -125,7 +136,8 @@ bool parseSearchString( QString const & str, QStringList & indexWords, QRegExp & searchRegExp, int searchMode, bool matchCase, int distanceBetweenWords, - bool & hasCJK ) + bool & hasCJK, + bool ignoreWordsOrder ) { searchWords.clear(); indexWords.clear(); @@ -149,7 +161,7 @@ bool parseSearchString( QString const & str, QStringList & indexWords, { tokenizeCJK( indexWords, wordRegExp, list ); // QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); - searchString = makeHiliteRegExpString( indexWords, searchMode, distanceBetweenWords, hasCJK ); + searchString = makeHiliteRegExpString( list, searchMode, distanceBetweenWords, hasCJK , ignoreWordsOrder); } else { @@ -159,7 +171,7 @@ bool parseSearchString( QString const & str, QStringList & indexWords, // Make regexp for results hilite QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); - searchString = makeHiliteRegExpString( allWords, searchMode, distanceBetweenWords ); + searchString = makeHiliteRegExpString( allWords, searchMode, distanceBetweenWords,false, ignoreWordsOrder ); } searchRegExp = QRegExp( searchString, matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp2 ); searchRegExp.setMinimal( true ); @@ -443,25 +455,23 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QRegularExpression::UseUnicodePropertiesOption); QRegularExpression regSplit( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); + // RegExp mode + QRegularExpression searchRegularExpression; + if( searchMode == FTS::Wildcards ) + searchRegularExpression.setPattern( wildcardsToRegexp( searchRegexp.pattern() ) ); + else + searchRegularExpression.setPattern( searchRegexp.pattern() ); + QRegularExpression::PatternOptions patternOptions = + QRegularExpression::DotMatchesEverythingOption | QRegularExpression::UseUnicodePropertiesOption + | QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption; + if( searchRegexp.caseSensitivity() == Qt::CaseInsensitive ) + patternOptions |= QRegularExpression::CaseInsensitiveOption; + searchRegularExpression.setPatternOptions( patternOptions ); + if( !searchRegularExpression.isValid() ) + searchRegularExpression.setPattern( "" ); if( searchMode == FTS::Wildcards || searchMode == FTS::RegExp ) { - // RegExp mode - - QRegularExpression searchRegularExpression; - if( searchMode == FTS::Wildcards ) - searchRegularExpression.setPattern( wildcardsToRegexp( searchRegexp.pattern() ) ); - else - searchRegularExpression.setPattern( searchRegexp.pattern() ); - QRegularExpression::PatternOptions patternOptions = QRegularExpression::DotMatchesEverythingOption - | QRegularExpression::UseUnicodePropertiesOption - | QRegularExpression::MultilineOption - | QRegularExpression::InvertedGreedinessOption; - if( searchRegexp.caseSensitivity() == Qt::CaseInsensitive ) - patternOptions |= QRegularExpression::CaseInsensitiveOption; - searchRegularExpression.setPatternOptions( patternOptions ); - if( !searchRegularExpression.isValid() ) - searchRegularExpression.setPattern( "" ); for( int i = 0; i < offsets.size(); i++ ) { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) @@ -527,243 +537,92 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, if( ignoreDiacritics ) articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); - QStringList articleWords = articleText.split( needHandleBrackets ? splitWithBrackets : splitWithoutBrackets, - Qt::SkipEmptyParts ); + //QStringList articleWords = articleText.split( needHandleBrackets ? splitWithBrackets : splitWithoutBrackets, + // Qt::SkipEmptyParts ); - int wordsNum = articleWords.length(); - while ( pos < wordsNum ) + if(ignoreWordsOrder) { - QString s = articleWords[ pos ]; - bool breakSearch = false; - - QStringList parsedWords; - if( needHandleBrackets && ( s.indexOf( '(' ) >= 0 || s.indexOf( ')' ) >= 0 ) ) + bool allMatch = true; + foreach( QString word, words ) + { + if( containCJK( word ) || searchMode == FTS::PlainText ) { - // Handle brackets - QRegularExpressionMatch match_brackets = regBrackets.match( s ); - if( match_brackets.hasMatch() ) + if( !articleText.contains( word ) ) { - QStringList parts = match_brackets.capturedTexts(); - // Add empty strings for compatibility with QRegExp behaviour - for( int i = match_brackets.lastCapturedIndex() + 1; i < 6; i++ ) - parts.append( QString() ); - - QString word = parts[ 2 ] + parts[ 4 ]; // Brackets removed - parsedWords.append( word ); - - word = parts[ 1 ].remove( '(' ).remove( ')' ) - + parts[ 2 ] - + parts[ 3 ].remove( '(' ).remove( ')' ) - + parts[ 4 ] - + parts[ 5 ].remove( '(' ).remove( ')' ); // Brackets expansed - parsedWords.append( word ); + allMatch = false; + break; } + } + else if( searchMode == FTS::WholeWords) + { + QRegularExpression tmpReg( QString( "\b%1\b" ).arg( word ),QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption ); + if( !articleText.contains( tmpReg) ) + { + allMatch = false; + break; + } + } + + } + + if(!allMatch) + { + continue; + } + + if( distanceBetweenWords >= 0 ) + { + // the article text contains all the needed words. + // determine if distance restriction is meet + QRegularExpression replaceReg( QString( "(%1)" ).arg( words.join( '|' ) ), + QRegularExpression::CaseInsensitiveOption | + QRegularExpression::UseUnicodePropertiesOption ); + // use a string that could not be presented in the article. + articleText = articleText.replace( replaceReg, "=@XXXXX@=" ); + + auto hasCJK = false; + foreach(QString word,words) + { + if(containCJK( word )) + { + hasCJK = true; + break; + } + } + + //hascjk value ,perhaps should depend on each word + auto searchRegStr = makeHiliteRegExpString( Utils::repeat( "=@XXXXX@=", words.size() ), searchMode, distanceBetweenWords,hasCJK ); + QRegularExpression distanceOrderReg( searchRegStr, + QRegularExpression::CaseInsensitiveOption | + QRegularExpression::UseUnicodePropertiesOption ); + // use a string that could not be presented in the article. + if(articleText.contains(distanceOrderReg)) + { + if( headword.isEmpty() ) + offsetsForHeadwords.append( offsets.at( i ) ); else - parsedWords = s.split( regSplit, Qt::SkipEmptyParts ); + foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + + results++; + if( maxResults > 0 && results >= maxResults ) + break; } - else - parsedWords.append( s ); - - int n; - for( n = 0; n < parsedWords.size(); n++ ) - { - auto parsed_word = parsedWords.at( n ); - if( ignoreWordsOrder ) - { - int i; - for( i = 0; i < wordsList.size(); i++ ) - { - if( wordsList.at( i ).second ) - { - if( ( searchMode == FTS::WholeWords && parsed_word.compare( wordsList.at( i ).first, cs ) == 0 ) - || ( searchMode == FTS::PlainText && parsed_word.contains( wordsList.at( i ).first, cs ) ) ) - { - wordsList[ i ].second = false; - - if( parsedWords.size() > 1 ) - { - QString wordToHilite = s; - while( !wordToHilite.isEmpty() && ( wordToHilite.at( 0 ) == '(' || wordToHilite.at( 0 ) == ')' ) ) - wordToHilite.remove( 0, 1 ); - while( !wordToHilite.isEmpty() && ( wordToHilite.endsWith( '(' ) || wordToHilite.endsWith( ')' ) ) ) - wordToHilite.chop( 1 ); - order.append( wordToHilite.replace( '(', "\\(" ).replace( ')', "\\)" ) ); - } - else - order.append( wordsList.at( i ).first ); - - break; - } - } - } - if( i < wordsList.size() ) - { - // Word found - - matchWordNom += 1; - - if( matchWordNom == 1 ) - { - // Store position to remake search if sequence will not be found - nextNotFoundPos = pos + 1; - } - - if( matchWordNom >= words.size() ) - { - // All words are found - // Store found words sequence and continue search - // It's nesessary for hilite search results - - // Check if such sequence already presented - int x; - for( x = 0; x < allOrders.size(); x++ ) - { - if( allOrders[ x ] == order ) - break; - } - if( x >= allOrders.size() ) - allOrders.append( order ); - - order.clear(); - - matchWordNom = 0; - unmatchWordNom = 0; - for( int i = 0; i < wordsList.size(); i++ ) - wordsList[ i ].second = true; - nextNotFoundPos = 0; - - break; - } - - unmatchWordNom = 0; - break; - } - else - if( matchWordNom > 0 && n >= parsedWords.size() - 1 ) - { - unmatchWordNom += 1; - if( distanceBetweenWords >= 0 && unmatchWordNom > distanceBetweenWords ) - { - // Sequence broken, clear all counters - matchWordNom = 0; - unmatchWordNom = 0; - for( int i = 0; i < wordsList.size(); i++ ) - wordsList[ i ].second = true; - order.clear(); - } - } - } - else - { - //for cjk word, FTS::WholeWords and FTS::PlainText actually have same effect. - auto match_word = words.at( matchWordNom ); - bool hasCJK = containCJK( match_word ); - if( ( searchMode == FTS::WholeWords && - ( ( !hasCJK&& parsed_word.compare( match_word, cs ) == 0 ) || - ( hasCJK && parsed_word.contains( match_word, cs ) ) ) ) - || ( searchMode == FTS::PlainText && parsed_word.contains( match_word, cs ) ) ) - { - matchWordNom += 1; - - if( matchWordNom == 1 ) - { - // Store position to remake search if sequence will not be found - nextNotFoundPos = pos + 1; - } - - if( needHandleBrackets ) - { - if( parsedWords.size() > 1 ) - { - QString wordToHilite = s; - while( !wordToHilite.isEmpty() && ( wordToHilite.at( 0 ) == '(' || wordToHilite.at( 0 ) == ')' ) ) - wordToHilite.remove( 0, 1 ); - while( !wordToHilite.isEmpty() && ( wordToHilite.endsWith( '(' ) || wordToHilite.endsWith( ')' ) ) ) - wordToHilite.chop( 1 ); - order.append( wordToHilite.replace( '(', "\\(" ).replace( ')', "\\)" ) ); - } - else - order.append( words.at( matchWordNom - 1 ) ); - } - - if( matchWordNom >= words.size() ) - { - // All words are found - if( needHandleBrackets ) - { - if( allOrders.isEmpty() ) - allOrders.append( words ); - - // Check if such sequence already presented - int x; - for( x = 0; x < allOrders.size(); x++ ) - { - if( allOrders[ x ] == order ) - break; - } - if( x >= allOrders.size() ) - allOrders.append( order ); - - matchWordNom = 0; - unmatchWordNom = 0; - order.clear(); - nextNotFoundPos = 0; - } - else - breakSearch = true; - break; - } - unmatchWordNom = 0; - break; - } - else - if( matchWordNom > 0 && n >= parsedWords.size() - 1 ) - { - unmatchWordNom += 1; - if( distanceBetweenWords >= 0 && unmatchWordNom > distanceBetweenWords ) - { - matchWordNom = 0; - unmatchWordNom = 0; - if( needHandleBrackets ) - order.clear(); - } - } - } - } - if( breakSearch ) - break; - if( nextNotFoundPos > 0 && matchWordNom == 0 ) - { - pos = nextNotFoundPos; - nextNotFoundPos = 0; - } - else - pos += 1; + } + } - - if( !allOrders.isEmpty() || matchWordNom >= words.size() ) + else { - QStringList hiliteReg; - if( !allOrders.isEmpty() ) + if( articleText.contains( searchRegularExpression ) ) { - for( int i = 0; i < allOrders.size(); i++ ) - { - QString hiliteStr = makeHiliteRegExpString( allOrders.at( i ), searchMode, distanceBetweenWords ); - hiliteReg.append( hiliteStr ); - } - allOrders.clear(); + if( headword.isEmpty() ) + offsetsForHeadwords.append( offsets.at( i ) ); + else + foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + + results++; + if( maxResults > 0 && results >= maxResults ) + break; } - if( headword.isEmpty() ) - { - offsetsForHeadwords.append( offsets.at( i ) ); - hiliteRegExps.append( hiliteReg ); - } - else - foundHeadwords->append( FTS::FtsHeadword( headword, id, hiliteReg, matchCase ) ); - - results++; - if( maxResults > 0 && results >= maxResults ) - break; } } } @@ -1150,7 +1009,7 @@ void FTSResultsRequest::run() QRegExp searchRegExp; if( !FtsHelpers::parseSearchString( searchString, indexWords, searchWords, searchRegExp, - searchMode, matchCase, distanceBetweenWords, hasCJK ) ) + searchMode, matchCase, distanceBetweenWords, hasCJK, ignoreWordsOrder ) ) { finish(); return; diff --git a/ftshelpers.hh b/ftshelpers.hh index 6d0fa29a..f477016d 100644 --- a/ftshelpers.hh +++ b/ftshelpers.hh @@ -55,7 +55,8 @@ bool parseSearchString( QString const & str, QStringList & IndexWords, QRegExp & searchRegExp, int searchMode, bool matchCase, int distanceBetweenWords, - bool & hasCJK ); + bool & hasCJK, + bool ignoreWordsOrder = false ); void parseArticleForFts( uint32_t articleAddress, QString & articleText, QMap< QString, QVector< uint32_t > > & words, diff --git a/fulltextsearch.cc b/fulltextsearch.cc index 749a2a94..cb87124b 100644 --- a/fulltextsearch.cc +++ b/fulltextsearch.cc @@ -351,7 +351,7 @@ void FullTextSearchDialog::accept() searchRegExp, mode, ui.matchCase->isChecked(), distanceBetweenWords, - hasCJK ) ) + hasCJK, ignoreWordsOrder ) ) { // if( hasCJK && ( mode == WholeWords || mode == PlainText ) ) // { diff --git a/utils.hh b/utils.hh index bfd71a79..be5c2563 100644 --- a/utils.hh +++ b/utils.hh @@ -118,6 +118,16 @@ inline QString json2String( const QJsonObject & json ) return QString( QJsonDocument( json ).toJson( QJsonDocument::Compact ) ); } +inline QStringList repeat( const QString str, const int times ) +{ + QStringList list; + for( int i = 0; i < times; i++ ) + { + list << str; + } + return list; +} + namespace AtomicInt { From c3abe99162c25688afb69ac1787211330824bfae Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 4 Jun 2022 17:32:33 +0800 Subject: [PATCH 064/125] feat: cjk regex match --- ftshelpers.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ftshelpers.cc b/ftshelpers.cc index e1b51641..2ac57cba 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -44,6 +44,12 @@ static QString makeHiliteRegExpString( QStringList const & words, QString searchString( "(" ); QString stripWords( "(?:\\W+\\w+){0," ); + + if( hasCJK ) + { + stripWords = "(?:[\\W\\w]){0,"; + } + if( distanceBetweenWords >= 0 ) stripWords += QString::number( distanceBetweenWords ); stripWords += "}"; From ee2fc14311af880f56a238a66de3f67f1fe61eab Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 4 Jun 2022 23:55:04 +0800 Subject: [PATCH 065/125] fix: indexSearch should pass the searchregex the current logic will use an empty regex ,make it will match any to-be-checked article --- ftshelpers.cc | 6 +++--- ftshelpers.hh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 2ac57cba..e155e06f 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -644,7 +644,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, sptr< ChunkedStorage::Reader > chunks, QStringList & indexWords, - QStringList & searchWords ) + QStringList & searchWords, QRegExp & regexp ) { // Find articles which contains all requested words @@ -714,7 +714,7 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, dict.sortArticlesOffsetsForFTS( offsets, isCancelled ); - checkArticles( offsets, searchWords ); + checkArticles( offsets, searchWords, regexp ); } void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, @@ -1048,7 +1048,7 @@ void FTSResultsRequest::run() else { if( searchMode == FTS::WholeWords ) - indexSearch( ftsIndex, chunks, indexWords, searchWords ); + indexSearch( ftsIndex, chunks, indexWords, searchWords, searchRegExp ); else fullIndexSearch( ftsIndex, chunks, indexWords, searchWords, searchRegExp ); } diff --git a/ftshelpers.hh b/ftshelpers.hh index f477016d..e5c712b9 100644 --- a/ftshelpers.hh +++ b/ftshelpers.hh @@ -91,7 +91,7 @@ class FTSResultsRequest : public Dictionary::DataRequest void indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, sptr< ChunkedStorage::Reader > chunks, QStringList & indexWords, - QStringList & searchWords ); + QStringList & searchWords, QRegExp & regexp ); void combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, sptr< ChunkedStorage::Reader > chunks, From 8fb1e81d75a5b849528efaa06d3383d9e7fe591d Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 6 Jun 2022 20:11:32 +0800 Subject: [PATCH 066/125] style:add body : 100% --- article-style.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/article-style.css b/article-style.css index 0cd9103c..a7ea4d2a 100644 --- a/article-style.css +++ b/article-style.css @@ -1,13 +1,14 @@ /******** Global, non-dictionary-specific classes ***********/ -html{ - height:100%; +html { + height: 100%; } body { background: #fefdeb; font-family: Tahoma, Verdana, "Lucida Sans Unicode", sans-serif; + height: 100%; } /* This stylesheet is used to highligh current selection when doing a search. From aa93d9f2d90fc308a56f0036b6b7f577981a47eb Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 6 Jun 2022 20:16:13 +0800 Subject: [PATCH 067/125] opt: replace string() with QString::toStdstring --- aard.cc | 4 ++-- dsl.cc | 2 +- mdictparser.hh | 2 +- mdx.cc | 14 +++++++------- slob.cc | 6 +++--- zim.cc | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/aard.cc b/aard.cc index d6a4eab7..df1d92fa 100644 --- a/aard.cc +++ b/aard.cc @@ -429,7 +429,7 @@ void AardDictionary::loadArticle( quint32 address, while( 1 ) { - articleText = string( QObject::tr( "Article loading error" ).toUtf8().constData() ); + articleText = QObject::tr( "Article loading error" ).toStdString(); try { Mutex::Lock _( aardMutex ); @@ -521,7 +521,7 @@ void AardDictionary::loadArticle( quint32 address, articleText = convert( articleText ); } else - articleText = string( QObject::tr( "Article decoding error" ).toUtf8().constData() ); + articleText = QObject::tr( "Article decoding error" ).toStdString(); // See Issue #271: A mechanism to clean-up invalid HTML cards. string cleaner = """""""""""" diff --git a/dsl.cc b/dsl.cc index 84bd687e..164a42fe 100644 --- a/dsl.cc +++ b/dsl.cc @@ -1697,7 +1697,7 @@ void DslArticleRequest::run() { gdWarning( "DSL: Failed loading article from \"%s\", reason: %s\n", dict.getName().c_str(), ex.what() ); articleText = string( "" ) - + string( QObject::tr( "Article loading error" ).toUtf8().constData() ) + + QObject::tr( "Article loading error" ).toStdString() + ""; } diff --git a/mdictparser.hh b/mdictparser.hh index b8dee43e..bd754f56 100644 --- a/mdictparser.hh +++ b/mdictparser.hh @@ -173,7 +173,7 @@ public: { QString s = QString::fromUtf8( article.c_str() ); substituteStylesheet( s, styleSheets ); - return string( s.toUtf8().constData() ); + return s.toStdString(); } protected: diff --git a/mdx.cc b/mdx.cc index 4165d0ab..caa3ec0f 100644 --- a/mdx.cc +++ b/mdx.cc @@ -977,7 +977,7 @@ void MdxDictionary::loadArticle( uint32_t offset, string & articleText, bool noF if( !noFilter ) article = filterResource( articleId, article ); - articleText = string( article.toUtf8().constData() ); + articleText = article.toStdString(); } QString & MdxDictionary::filterResource( QString const & articleId, QString & article ) @@ -1409,7 +1409,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f if ( !parser.open( i->c_str() ) ) continue; - string title = string( parser.title().toUtf8().constData() ); + string title = parser.title().toStdString(); initializing.indexingDictionary( title ); for ( vector< string >::const_iterator mddIter = dictFiles.begin() + 1; @@ -1440,7 +1440,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f // then the encoding { - string encoding = string( parser.encoding().toUtf8().constData() ); + string encoding = parser.encoding().toStdString(); idx.write< uint32_t >( encoding.size() ); idx.write( encoding.data(), encoding.size() ); } @@ -1457,7 +1457,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f // Save dictionary description if there's one { - string description = string( parser.description().toUtf8().constData() ); + string description = parser.description().toStdString(); idxHeader.descriptionAddress = chunks.startNewBlock(); chunks.addToBlock( description.c_str(), description.size() + 1 ); idxHeader.descriptionSize = description.size() + 1; @@ -1491,7 +1491,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f mddIndices.push_back( mddIndexedWords ); // Save filename for .mdd files only QFileInfo fi( mddParser->filename() ); - mddFileNames.push_back( string( fi.fileName().toUtf8().constData() ) ); + mddFileNames.push_back( fi.fileName().toStdString() ); mddParsers.pop_front(); } @@ -1514,8 +1514,8 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f for ( MdictParser::StyleSheets::const_iterator iter = styleSheets.begin(); iter != styleSheets.end(); ++iter ) { - string styleBegin( iter->second.first.toUtf8().constData() ); - string styleEnd( iter->second.second.toUtf8().constData() ); + string styleBegin(iter->second.first.toStdString()); + string styleEnd( iter->second.second.toStdString() ); // key idx.write( iter->first ); diff --git a/slob.cc b/slob.cc index 7851ebec..9b952e6a 100644 --- a/slob.cc +++ b/slob.cc @@ -694,12 +694,12 @@ SlobDictionary::SlobDictionary( string const & id, // Read dictionary name - dictionaryName = string( sf.getDictionaryName().toUtf8().constData() ); + dictionaryName = sf.getDictionaryName().toStdString(); if( dictionaryName.empty() ) { QString name = QDir::fromNativeSeparators( FsEncoding::decode( dictionaryFiles[ 0 ].c_str() ) ); int n = name.lastIndexOf( '/' ); - dictionaryName = string( name.mid( n + 1 ).toUtf8().constData() ); + dictionaryName = name.mid( n + 1 ).toStdString(); } // Full-text search parameters @@ -799,7 +799,7 @@ void SlobDictionary::loadArticle( quint32 address, articleText = convert( articleText, entry ); } else - articleText = string( QObject::tr( "Article decoding error" ).toUtf8().constData() ); + articleText = QObject::tr( "Article decoding error" ).toStdString(); // See Issue #271: A mechanism to clean-up invalid HTML cards. string cleaner = """""""""""" diff --git a/zim.cc b/zim.cc index e054778d..6dc8a9b1 100644 --- a/zim.cc +++ b/zim.cc @@ -769,7 +769,7 @@ ZimDictionary::ZimDictionary( string const & id, { QString name = QDir::fromNativeSeparators( FsEncoding::decode( dictionaryFiles[ 0 ].c_str() ) ); int n = name.lastIndexOf( '/' ); - dictionaryName = string( name.mid( n + 1 ).toUtf8().constData() ); + dictionaryName = name.mid( n + 1 ).toStdString(); } else { From 5f856bf2aeee8d4416b968bb698089a3ca4d03a9 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 7 Jun 2022 08:05:49 +0800 Subject: [PATCH 068/125] refactor getqueryword --- article_netmgr.cc | 5 ++--- articleview.cc | 5 ++--- utils.hh | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index 6d0d3f5c..5a172289 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -513,11 +513,10 @@ void LocalSchemeHandler::requestStarted(QWebEngineUrlRequestJob *requestJob) request.setUrl( url ); //all the url reached here must be either gdlookup or bword scheme. - auto queryWord = Utils::Url::getQueryWord( url ); - auto word = queryWord.second; + auto [valid, word] = Utils::Url::getQueryWord( url ); // or the condition can be (!queryWord.first || word.isEmpty()) // ( queryWord.first && word.isEmpty() ) is only part of the above condition. - if( queryWord.first && word.isEmpty() ) + if( valid && word.isEmpty() ) { // invalid gdlookup url. return; diff --git a/articleview.cc b/articleview.cc index b1eace83..3d426d89 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1113,9 +1113,8 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, audioPlayer->stop(); qDebug() << "open link url:" << url; - auto queryWord = Utils::Url::getQueryWord( url ); - auto word = queryWord.second; - if( queryWord.first && word.isEmpty() ) + auto [valid, word] = Utils::Url::getQueryWord( url ); + if( valid && word.isEmpty() ) { // invalid gdlookup url. return; diff --git a/utils.hh b/utils.hh index be5c2563..a91d0e6e 100644 --- a/utils.hh +++ b/utils.hh @@ -224,7 +224,24 @@ inline std::pair< bool, QString > getQueryWord( QUrl const & url ) if( url.scheme().compare( "bword" ) == 0 ) { validScheme = true; - word = url.path().mid( 1 ); + + auto path = url.path(); + // url like this , bword:word or bword://localhost/word + if( !path.isEmpty() ) + { + //url,bword://localhost/word + if( path.startsWith( "/" ) ) + word = url.path().mid( 1 ); + } + else + { + // url looks like this, bword://word,or bword://localhost + auto host = url.host(); + if( host != "localhost" ) + { + word = host; + } + } } return std::make_pair( validScheme, word ); } From 9daff6ae371b8319470a4281bbb1d9d81bcd9ce9 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 7 Jun 2022 08:11:10 +0800 Subject: [PATCH 069/125] support mdex builtin link entry: --- articleview.cc | 2 +- main.cc | 2 +- mainwindow.cc | 1 + utils.hh | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/articleview.cc b/articleview.cc index 3d426d89..7e7120bb 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1125,7 +1125,7 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, if( url.scheme().compare( "gdpicture" ) == 0 ) ui.definition->load( url ); else - if ( url.scheme().compare( "bword" ) == 0 ) + if ( url.scheme().compare( "bword" ) == 0 || url.scheme().compare( "entry" ) == 0 ) { if( Utils::Url::hasQueryItem( ref, "dictionaries" ) ) { diff --git a/main.cc b/main.cc index 958e2b5d..1d32034d 100644 --- a/main.cc +++ b/main.cc @@ -253,7 +253,7 @@ int main( int argc, char ** argv ) #endif - QStringList localSchemes={"gdlookup","gdau","gico","qrcx","bres","bword","gdprg","gdvideo","gdpicture","gdtts","ifr"}; + QStringList localSchemes={"gdlookup","gdau","gico","qrcx","bres","bword","gdprg","gdvideo","gdpicture","gdtts","ifr", "entry"}; for (int i = 0; i < localSchemes.size(); ++i) { diff --git a/mainwindow.cc b/mainwindow.cc index f8103eae..29f2bee3 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -164,6 +164,7 @@ MainWindow::MainWindow( Config::Class & cfg_ ): localSchemeHandler = new LocalSchemeHandler( articleNetMgr ); QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "gdlookup", localSchemeHandler ); QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "bword", localSchemeHandler ); + QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "entry", localSchemeHandler ); iframeSchemeHandler = new IframeSchemeHandler( this ); QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "ifr", iframeSchemeHandler ); diff --git a/utils.hh b/utils.hh index a91d0e6e..eb4fee08 100644 --- a/utils.hh +++ b/utils.hh @@ -221,7 +221,7 @@ inline std::pair< bool, QString > getQueryWord( QUrl const & url ) word = url.path().mid( 1 ); } } - if( url.scheme().compare( "bword" ) == 0 ) + if( url.scheme().compare( "bword" ) == 0 || url.scheme().compare( "entry" ) == 0 ) { validScheme = true; From 88b265765f3dec5365a82e15f3f0b4257576b564 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 7 Jun 2022 21:22:37 +0800 Subject: [PATCH 070/125] add entry:// link support --- article_netmgr.cc | 22 ++++++++++++++++++---- globalbroadcaster.cpp | 8 ++++++++ globalbroadcaster.h | 6 ++++++ resources/gd-custom.js | 5 +++++ utils.hh | 4 +++- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index 5a172289..32c65ea9 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -8,6 +8,7 @@ #include "gddebug.hh" #include "utils.hh" #include +#include "globalbroadcaster.h" using std::string; @@ -249,11 +250,22 @@ QNetworkReply * ArticleNetworkAccessManager::getArticleReply( QNetworkRequest co } sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( - QUrl const & url, QString & contentType ) + QUrl const & resUrl, QString & contentType ) { - GD_DPRINTF( "getResource: %ls", url.toString().toStdWString().c_str() ); - GD_DPRINTF( "scheme: %ls", url.scheme().toStdWString().c_str() ); - GD_DPRINTF( "host: %ls", url.host().toStdWString().c_str() ); + GD_DPRINTF( "getResource: %ls", resUrl.toString().toStdWString().c_str() ); + GD_DPRINTF( "scheme: %ls", resUrl.scheme().toStdWString().c_str() ); + GD_DPRINTF( "host: %ls", resUrl.host().toStdWString().c_str() ); + + QUrl url = resUrl; + if( url.scheme() == "bword" || url.scheme() == "entry" ) + { + url.setScheme( "gdlookup" ); + url.setHost( "localhost" ); + url.setPath( "" ); + auto [ valid, word ] = Utils::Url::getQueryWord( resUrl ); + Utils::Url::addQueryItem( url, "word", word ); + Utils::Url::addQueryItem( url, "group", QString( "%1" ).arg( GlobalBroadcaster::instance()->getGroupId() ) ); + } if ( url.scheme() == "gdlookup" ) { @@ -273,6 +285,8 @@ sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( bool groupIsValid = false; unsigned group = Utils::Url::queryItemValue( url, "group" ).toUInt( &groupIsValid ); + + GlobalBroadcaster::instance()->setGroupId(group); QString dictIDs = Utils::Url::queryItemValue( url, "dictionaries" ); if( !dictIDs.isEmpty() ) diff --git a/globalbroadcaster.cpp b/globalbroadcaster.cpp index 885bf89a..7d2468d7 100644 --- a/globalbroadcaster.cpp +++ b/globalbroadcaster.cpp @@ -29,4 +29,12 @@ void GlobalBroadcaster::addWhitelist(QString url){ bool GlobalBroadcaster::existedInWhitelist(QString url){ return std::find(whitelist.begin(), whitelist.end(), url) != whitelist.end(); } + +void GlobalBroadcaster::setGroupId(int groupId){ + this->groupId = groupId; +} + +int GlobalBroadcaster::getGroupId(){ + return groupId; +} // namespace global diff --git a/globalbroadcaster.h b/globalbroadcaster.h index 3014a46e..c53bc50f 100644 --- a/globalbroadcaster.h +++ b/globalbroadcaster.h @@ -17,6 +17,8 @@ class GlobalBroadcaster : public QObject private: Config::Preferences * preference; std::vector whitelist; + int groupId; + public: void setPreference( Config::Preferences * _pre ); Config::Preferences * getPreference(); @@ -24,6 +26,10 @@ public: void addWhitelist(QString host); bool existedInWhitelist(QString host); static GlobalBroadcaster * instance(); + + //store the latest groupId; + void setGroupId(int groupId); + int getGroupId(); signals: void dictionaryChanges( ActiveDictIds ad ); }; diff --git a/resources/gd-custom.js b/resources/gd-custom.js index ae07cf31..d7557151 100644 --- a/resources/gd-custom.js +++ b/resources/gd-custom.js @@ -6,6 +6,11 @@ $(function() { if ('string' != typeof(link)) { return; } + + if(link.indexOf("javascript:")>=0){ + return; + } + if(link.indexOf(":")>=0){ emitClickedEvent(link); return false; diff --git a/utils.hh b/utils.hh index eb4fee08..6f4b0b1b 100644 --- a/utils.hh +++ b/utils.hh @@ -231,7 +231,9 @@ inline std::pair< bool, QString > getQueryWord( QUrl const & url ) { //url,bword://localhost/word if( path.startsWith( "/" ) ) - word = url.path().mid( 1 ); + word = path.mid( 1 ); + else + word = path; } else { From 547f2a4cd286c4711a69b62e3c6bdc89b1fd7da2 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 8 Jun 2022 08:19:23 +0800 Subject: [PATCH 071/125] remember last link's groupId and muted dictionary --- article_netmgr.cc | 21 ++++----------------- articlewebpage.cpp | 25 ++++++++++++++++++++++++- articlewebpage.h | 7 +++++++ globalbroadcaster.cpp | 22 ++++++++-------------- globalbroadcaster.h | 4 ---- mainwindow.cc | 1 + 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/article_netmgr.cc b/article_netmgr.cc index 32c65ea9..e0e23650 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -250,22 +250,11 @@ QNetworkReply * ArticleNetworkAccessManager::getArticleReply( QNetworkRequest co } sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( - QUrl const & resUrl, QString & contentType ) + QUrl const & url, QString & contentType ) { - GD_DPRINTF( "getResource: %ls", resUrl.toString().toStdWString().c_str() ); - GD_DPRINTF( "scheme: %ls", resUrl.scheme().toStdWString().c_str() ); - GD_DPRINTF( "host: %ls", resUrl.host().toStdWString().c_str() ); - - QUrl url = resUrl; - if( url.scheme() == "bword" || url.scheme() == "entry" ) - { - url.setScheme( "gdlookup" ); - url.setHost( "localhost" ); - url.setPath( "" ); - auto [ valid, word ] = Utils::Url::getQueryWord( resUrl ); - Utils::Url::addQueryItem( url, "word", word ); - Utils::Url::addQueryItem( url, "group", QString( "%1" ).arg( GlobalBroadcaster::instance()->getGroupId() ) ); - } + GD_DPRINTF( "getResource: %ls", url.toString().toStdWString().c_str() ); + GD_DPRINTF( "scheme: %ls", url.scheme().toStdWString().c_str() ); + GD_DPRINTF( "host: %ls", url.host().toStdWString().c_str() ); if ( url.scheme() == "gdlookup" ) { @@ -285,8 +274,6 @@ sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( bool groupIsValid = false; unsigned group = Utils::Url::queryItemValue( url, "group" ).toUInt( &groupIsValid ); - - GlobalBroadcaster::instance()->setGroupId(group); QString dictIDs = Utils::Url::queryItemValue( url, "dictionaries" ); if( !dictIDs.isEmpty() ) diff --git a/articlewebpage.cpp b/articlewebpage.cpp index 9b782f8d..bf686114 100644 --- a/articlewebpage.cpp +++ b/articlewebpage.cpp @@ -1,15 +1,38 @@ #include "articlewebpage.h" +#include "utils.hh" ArticleWebPage::ArticleWebPage(QObject *parent) : QWebEnginePage{parent} { } -bool ArticleWebPage::acceptNavigationRequest( const QUrl & url, NavigationType type, bool isMainFrame ) +bool ArticleWebPage::acceptNavigationRequest( const QUrl & resUrl, NavigationType type, bool isMainFrame ) { + QUrl url = resUrl; + if( url.scheme() == "bword" || url.scheme() == "entry" ) + { + url.setScheme( "gdlookup" ); + url.setHost( "localhost" ); + url.setPath( "" ); + auto [ valid, word ] = Utils::Url::getQueryWord( resUrl ); + Utils::Url::addQueryItem( url, "word", word ); + Utils::Url::addQueryItem( url, "group", lastReq.group ); + Utils::Url::addQueryItem( url, "muted", lastReq.mutedDicts ); + setUrl( url ); + return false; + } + + //save current gdlookup's values. + if( url.scheme() == "gdlookup" ) + { + lastReq.group = Utils::Url::queryItemValue( url, "group" ); + lastReq.mutedDicts = Utils::Url::queryItemValue( url, "muted" ); + } + if( type == QWebEnginePage::NavigationTypeLinkClicked ) { emit linkClicked( url ); return true; } + return QWebEnginePage::acceptNavigationRequest( url, type, isMainFrame ); } diff --git a/articlewebpage.h b/articlewebpage.h index 3e1d32d6..19027148 100644 --- a/articlewebpage.h +++ b/articlewebpage.h @@ -3,6 +3,11 @@ #include +struct LastReqInfo{ + QString group; + QString mutedDicts; +}; + class ArticleWebPage : public QWebEnginePage { Q_OBJECT @@ -12,6 +17,8 @@ signals: void linkClicked( const QUrl & url ); protected: virtual bool acceptNavigationRequest( const QUrl & url, NavigationType type, bool isMainFrame ); +private: + LastReqInfo lastReq; }; #endif // ARTICLEWEBPAGE_H diff --git a/globalbroadcaster.cpp b/globalbroadcaster.cpp index 7d2468d7..bcac205f 100644 --- a/globalbroadcaster.cpp +++ b/globalbroadcaster.cpp @@ -20,21 +20,15 @@ Config::Preferences * GlobalBroadcaster::getPreference() return preference; } -void GlobalBroadcaster::addWhitelist(QString url){ - whitelist.push_back(url); - auto baseUrl=::getHostBase(url); - whitelist.push_back(baseUrl); +void GlobalBroadcaster::addWhitelist( QString url ) +{ + whitelist.push_back( url ); + auto baseUrl = ::getHostBase( url ); + whitelist.push_back( baseUrl ); } -bool GlobalBroadcaster::existedInWhitelist(QString url){ - return std::find(whitelist.begin(), whitelist.end(), url) != whitelist.end(); -} - -void GlobalBroadcaster::setGroupId(int groupId){ - this->groupId = groupId; -} - -int GlobalBroadcaster::getGroupId(){ - return groupId; +bool GlobalBroadcaster::existedInWhitelist( QString url ) +{ + return std::find( whitelist.begin(), whitelist.end(), url ) != whitelist.end(); } // namespace global diff --git a/globalbroadcaster.h b/globalbroadcaster.h index c53bc50f..6e6992c2 100644 --- a/globalbroadcaster.h +++ b/globalbroadcaster.h @@ -17,7 +17,6 @@ class GlobalBroadcaster : public QObject private: Config::Preferences * preference; std::vector whitelist; - int groupId; public: void setPreference( Config::Preferences * _pre ); @@ -27,9 +26,6 @@ public: bool existedInWhitelist(QString host); static GlobalBroadcaster * instance(); - //store the latest groupId; - void setGroupId(int groupId); - int getGroupId(); signals: void dictionaryChanges( ActiveDictIds ad ); }; diff --git a/mainwindow.cc b/mainwindow.cc index 29f2bee3..fb4ae931 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -1417,6 +1417,7 @@ void MainWindow::updateGroupList() groupList->fill( groupInstances ); groupList->setCurrentGroup( cfg.lastMainGroupId ); + updateCurrentGroupProperty(); updateDictionaryBar(); From 3e6c85b404fdea58c3fdbb4f2fc279ed2f012ee1 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 8 Jun 2022 20:55:01 +0800 Subject: [PATCH 072/125] add dict's margin back to lingos style --- article-style-st-lingoes.css | 1 + article-style.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/article-style-st-lingoes.css b/article-style-st-lingoes.css index 9f31b39e..455aa75b 100644 --- a/article-style-st-lingoes.css +++ b/article-style-st-lingoes.css @@ -97,4 +97,5 @@ body { clear: both; border-top: 1px solid #92b0dd; + margin-bottom: 1em; } diff --git a/article-style.css b/article-style.css index a7ea4d2a..959edbaa 100644 --- a/article-style.css +++ b/article-style.css @@ -531,7 +531,7 @@ div.xdxf /************* MDict dictionaries **************/ .mdict { - /* margin-top: 1em; */ + } .mdict a[name] From 4ce03e9415bee694c2b0d8ab745077c6a29bb65e Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 8 Jun 2022 21:13:07 +0800 Subject: [PATCH 073/125] html unescape --- htmlescape.cc | 29 +++++++++++++++++++++++++++++ htmlescape.hh | 3 +++ mdictparser.cc | 2 +- utils.hh | 7 ------- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/htmlescape.cc b/htmlescape.cc index 59086e8c..bb2c65e6 100644 --- a/htmlescape.cc +++ b/htmlescape.cc @@ -157,6 +157,35 @@ QString unescape( QString const & str, bool saveFormat ) return str; } +QString fromHtmlEscaped( QString const & str){ + QString retVal = str; + QRegularExpression regExp("(?\\<\\;)|(?\\>\\;)|(?\\&\\;)|(?\\"\\;)", QRegularExpression::PatternOption::CaseInsensitiveOption); + auto match = regExp.match(str, 0); + + while (match.hasMatch()) + { + if (!match.captured("lt").isEmpty()) + { + retVal.replace(match.capturedStart("lt"), match.capturedLength("lt"), "<"); + } + else if (!match.captured("gt").isEmpty()) + { + retVal.replace(match.capturedStart("gt"), match.capturedLength("gt"), ">"); + } + else if (!match.captured("amp").isEmpty()) + { + retVal.replace(match.capturedStart("amp"), match.capturedLength("amp"), "&"); + } + else if (!match.captured("quot").isEmpty()) + { + retVal.replace(match.capturedStart("quot"), match.capturedLength("quot"), "\""); + } + match = regExp.match(retVal, match.capturedStart() + 1); + } + + return retVal; +} + string unescapeUtf8( const string &str, bool saveFormat ) { return string( unescape( QString::fromUtf8( str.c_str(), str.size() ) ).toUtf8().data(), saveFormat ); diff --git a/htmlescape.hh b/htmlescape.hh index f86e4136..39f3d161 100644 --- a/htmlescape.hh +++ b/htmlescape.hh @@ -4,6 +4,7 @@ #ifndef __HTMLESCAPE_HH_INCLUDED__ #define __HTMLESCAPE_HH_INCLUDED__ +#include #include namespace Html { @@ -24,6 +25,8 @@ string escapeForJavaScript( string const & ); // Replace html entities QString unescape( QString const & str, bool saveFormat = false ); + +QString fromHtmlEscaped( QString const & str); string unescapeUtf8( string const & str, bool saveFormat = false ); } diff --git a/mdictparser.cc b/mdictparser.cc index f3124a46..a4ec7142 100644 --- a/mdictparser.cc +++ b/mdictparser.cc @@ -374,7 +374,7 @@ bool MdictParser::readHeader( QDataStream & in ) { #if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) styleSheets_[ lines[ i ].toInt() ] = - pair< QString, QString >( Html::unescape( lines[ i + 1 ] ), Html::unescape( lines[ i + 2 ] ) ); + pair< QString, QString >( Html::fromHtmlEscaped( lines[ i + 1 ] ), Html::fromHtmlEscaped( lines[ i + 2 ] ) ); #else styleSheets_[ lines[ i ].toInt() ] = pair< QString, QString >( lines[ i + 1 ], lines[ i + 2 ] ); #endif diff --git a/utils.hh b/utils.hh index 6f4b0b1b..a12175af 100644 --- a/utils.hh +++ b/utils.hh @@ -78,13 +78,6 @@ inline QString rstripnull(const QString &str) { return ""; } -inline QString unescapeHtml(const QString &str) { - QTextDocument text; - text.setHtml(str); - return text.toPlainText(); -} - - inline bool isExternalLink(QUrl const &url) { return url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp" || url.scheme() == "mailto" || url.scheme() == "file" || url.toString().startsWith( "//" ); From 01da9c93ab31a883210a8417112cb8c44093f1f8 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 8 Jun 2022 21:16:04 +0800 Subject: [PATCH 074/125] github: changelog modify --- .github/workflows/macos.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7624d090..6601aefd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -75,8 +75,11 @@ jobs: mv ${targetName}.app ./tmp # --background "installer_background.png" create-dmg --volname "${targetName} Installer" --volicon "icons/macicon.icns" --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "${targetName}.app" 200 190 --hide-extension "${targetName}.app" --app-drop-link 600 185 --skip-jenkins "${targetName}.dmg" tmp/ - - + - name: Generate changelog + id: changelog + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} - name: Set outputs id: vars run: | @@ -122,4 +125,6 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} + ${{ steps.vars.outputs.COMMIT_SUMMARY }} + CHANGE LOGS: + ${{ steps.changelog.outputs.changelog }} From 1c27c2e71dcd280dbf275e14cba7767aef76c06d Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 8 Jun 2022 21:47:59 +0800 Subject: [PATCH 075/125] Revert "github: changelog modify" This reverts commit 01da9c93ab31a883210a8417112cb8c44093f1f8. --- .github/workflows/macos.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6601aefd..7624d090 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -75,11 +75,8 @@ jobs: mv ${targetName}.app ./tmp # --background "installer_background.png" create-dmg --volname "${targetName} Installer" --volicon "icons/macicon.icns" --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "${targetName}.app" 200 190 --hide-extension "${targetName}.app" --app-drop-link 600 185 --skip-jenkins "${targetName}.dmg" tmp/ - - name: Generate changelog - id: changelog - uses: metcalfc/changelog-generator@v3.0.0 - with: - myToken: ${{ secrets.GITHUB_TOKEN }} + + - name: Set outputs id: vars run: | @@ -125,6 +122,4 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} - CHANGE LOGS: - ${{ steps.changelog.outputs.changelog }} + ${{ steps.vars.outputs.COMMIT_SUMMARY }} From 10d0d8193b60b8ac5f06973742fdd0fbb1de6a2d Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Wed, 8 Jun 2022 19:01:35 +0300 Subject: [PATCH 076/125] setCurrentArticle(): return false if the article doesn't exist 201f11e65638365e7ef996bf43386581ff4bd075 missed this possibility and may have inadvertently changed the application behavior in a corner case. --- articleview.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/articleview.cc b/articleview.cc index 01711cbc..b0e2d1ae 100644 --- a/articleview.cc +++ b/articleview.cc @@ -760,19 +760,19 @@ bool ArticleView::setCurrentArticle( QString const & id, bool moveToIt ) return false; // No action on background page, scrollIntoView there don't work QString const dictionaryId = dictionaryIdFromScrollTo( id ); - if ( getArticlesList().contains( dictionaryId ) ) - { - if ( moveToIt ) - ui.definition->page()->mainFrame()->evaluateJavaScript( QString( "document.getElementById('%1').scrollIntoView(true);" ).arg( id ) ); + if( !getArticlesList().contains( dictionaryId ) ) + return false; - QMap< QString, QVariant > userData = ui.definition->history()-> - currentItem().userData().toMap(); - userData[ "currentArticle" ] = id; - ui.definition->history()->currentItem().setUserData( userData ); + if ( moveToIt ) + ui.definition->page()->mainFrame()->evaluateJavaScript( QString( "document.getElementById('%1').scrollIntoView(true);" ).arg( id ) ); + + QMap< QString, QVariant > userData = ui.definition->history()->currentItem().userData().toMap(); + userData[ "currentArticle" ] = id; + ui.definition->history()->currentItem().setUserData( userData ); + + ui.definition->page()->mainFrame()->evaluateJavaScript( + QString( "gdMakeArticleActive( '%1' );" ).arg( dictionaryId ) ); - ui.definition->page()->mainFrame()->evaluateJavaScript( - QString( "gdMakeArticleActive( '%1' );" ).arg( dictionaryId ) ); - } return true; } From 73e02719d33e9de9ada57b31e56a06c91bc1e68c Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Thu, 9 Jun 2022 12:47:59 +0800 Subject: [PATCH 077/125] Update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 6067cbe8..49f1ea9a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,9 @@ - add "send to anki" - right context menu actions, remove nonsense character like `OBJ`,punctuation etc. - epwing remove duplicate entries when index. +- replace throw() with noexcept +- replace string(***constData) with toStdString +- add built-in support for entry links in javascript files ## Until 2022-5-21 From 0c21dacc8ab04ae1631d4e11f1ccbcc0b79c395f Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 9 Jun 2022 20:15:02 +0800 Subject: [PATCH 078/125] fix : inspect element hang forever if opened by right context menu on the first time. --- article_inspect.cpp | 8 ++++++-- article_inspect.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/article_inspect.cpp b/article_inspect.cpp index e194fec1..71a0f8f3 100644 --- a/article_inspect.cpp +++ b/article_inspect.cpp @@ -3,7 +3,7 @@ #if (QT_VERSION > QT_VERSION_CHECK(6,0,0)) #include #endif -ArticleInspector::ArticleInspector( QWidget * parent ) : QWidget( parent, Qt::WindowType::Window ) +ArticleInspector::ArticleInspector( QWidget * parent ) : QWidget( parent, Qt::WindowType::Window ),firstTimeOpened(false) { setWindowTitle(tr("Inspect")); setAttribute( Qt::WidgetAttribute::WA_DeleteOnClose, false ); @@ -22,13 +22,17 @@ void ArticleInspector::setInspectPage( QWebEngineView * view ) viewContainer->page()->setInspectedPage(page); #if( QT_VERSION > QT_VERSION_CHECK( 6, 0, 0 ) ) // without this line, application will crash on qt6.2 ,see https://bugreports.qt.io/browse/QTBUG-101724 - if( view->lastContextMenuRequest() ) + if( view->lastContextMenuRequest() && firstTimeOpened ) { page->triggerAction( QWebEnginePage::InspectElement ); } #else page->triggerAction( QWebEnginePage::InspectElement ); #endif + if( !firstTimeOpened ) + { + firstTimeOpened = true; + } raise(); show(); } diff --git a/article_inspect.h b/article_inspect.h index 8c7b50c5..7be7c7a6 100644 --- a/article_inspect.h +++ b/article_inspect.h @@ -15,7 +15,9 @@ public: void setInspectPage( QWebEngineView * view); private: - + //used to record if the devtool was first time opened. + //if right click on the webpage and open inspect page on the first time ,the application has great possiblity to hang forever. + bool firstTimeOpened; virtual void closeEvent( QCloseEvent * ); }; From 3ed7ce5ccb55d248a866aad7e33dc36ab8ec4303 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 10 Jun 2022 20:44:59 +0800 Subject: [PATCH 079/125] syntaticly space is more reasonable than remove directly. --- htmlescape.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htmlescape.cc b/htmlescape.cc index bb2c65e6..946b1b6c 100644 --- a/htmlescape.cc +++ b/htmlescape.cc @@ -149,7 +149,7 @@ QString unescape( QString const & str, bool saveFormat ) { tmp.replace( QRegularExpression( "<(?:\\s*/?(?:div|h[1-6r]|q|p(?![alr])|br|li(?![ns])|td|blockquote|[uo]l|pre|d[dl]|nav|address))[^>]{0,}>", QRegularExpression::CaseInsensitiveOption ), " " ); - tmp.remove( QRegularExpression( "<[^>]*>" ) ); + tmp.replace( QRegularExpression( "<[^>]*>"), " "); } return QTextDocumentFragment::fromHtml( tmp.trimmed() ).toPlainText(); From 9ea83f1299ab54226e5635320f543c3cd77f0c73 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 11 Jun 2022 16:25:56 +0800 Subject: [PATCH 080/125] fix : control character cause qdomdocument can not parse attribute --- mdictparser.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mdictparser.cc b/mdictparser.cc index a4ec7142..bc8eaf90 100644 --- a/mdictparser.cc +++ b/mdictparser.cc @@ -336,6 +336,9 @@ bool MdictParser::readHeader( QDataStream & in ) headerTextUtf16.clear(); in.setByteOrder( QDataStream::BigEndian ); + //with this control character ,qt6.x can not parse attribute value. + headerText.remove(QRegularExpression("\\p{C}")); + QDomNamedNodeMap headerAttributes = parseHeaderAttributes( headerText ); encoding_ = headerAttributes.namedItem( "Encoding" ).toAttr().value(); From 880f2df1b01c48d8eb46636fbe3088254f00c51d Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 4 Jun 2022 22:13:29 +0800 Subject: [PATCH 081/125] clean code: remove useless code --- ftshelpers.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index e155e06f..aa053dd3 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -522,11 +522,6 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, if( Utils::AtomicInt::loadAcquire( isCancelled ) ) break; - int pos = 0; - int matchWordNom = 0; - int unmatchWordNom = 0; - int nextNotFoundPos = 0; - QVector< QStringList > allOrders; QStringList order; @@ -543,9 +538,6 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, if( ignoreDiacritics ) articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); - //QStringList articleWords = articleText.split( needHandleBrackets ? splitWithBrackets : splitWithoutBrackets, - // Qt::SkipEmptyParts ); - if(ignoreWordsOrder) { bool allMatch = true; From dcaebf4948a8632ac2bd723988b5575fa1e9bdcc Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 4 Jun 2022 23:22:14 +0800 Subject: [PATCH 082/125] imp. add QtConcurrent to checkArticle --- ftshelpers.cc | 197 ++++++++++++++++++++++++++++++++++++++++++++++++- ftshelpers.hh | 5 ++ goldendict.pro | 5 +- 3 files changed, 203 insertions(+), 4 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index aa053dd3..65d0bc1a 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -17,6 +17,7 @@ #include #include "wildcard.hh" +#include using std::vector; using std::string; @@ -445,7 +446,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QStringList const & words, QRegExp const & searchRegexp ) { - int results = 0; + // int results = 0; QString headword, articleText; QList< uint32_t > offsetsForHeadwords; QVector< QStringList > hiliteRegExps; @@ -617,7 +618,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, else foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); - results++; + ++results; if( maxResults > 0 && results >= maxResults ) break; } @@ -633,6 +634,195 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, } } +void FTSResultsRequest::checkSingleArticle( uint32_t offset, + QStringList const & words, + QRegExp const & searchRegexp ) +{ + qDebug()<<"checking"< offsetsForHeadwords; + QVector< QStringList > hiliteRegExps; + + QString id = QString::fromUtf8( dict.getId().c_str() ); + bool needHandleBrackets; + { + QString name = QString::fromUtf8( dict.getDictionaryFilenames()[ 0 ].c_str() ).toLower(); + needHandleBrackets = name.endsWith( ".dsl" ) || name.endsWith( ".dsl.dz" ); + } + + // RegExp mode + QRegularExpression searchRegularExpression; + if( searchMode == FTS::Wildcards ) + searchRegularExpression.setPattern( wildcardsToRegexp( searchRegexp.pattern() ) ); + else + searchRegularExpression.setPattern( searchRegexp.pattern() ); + QRegularExpression::PatternOptions patternOptions = + QRegularExpression::DotMatchesEverythingOption | QRegularExpression::UseUnicodePropertiesOption + | QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption; + if( searchRegexp.caseSensitivity() == Qt::CaseInsensitive ) + patternOptions |= QRegularExpression::CaseInsensitiveOption; + searchRegularExpression.setPatternOptions( patternOptions ); + if( !searchRegularExpression.isValid() ) + searchRegularExpression.setPattern( "" ); + + if( searchMode == FTS::Wildcards || searchMode == FTS::RegExp ) + { + // for( int i = 0; i < offsets.size(); i++ ) + { + if( Utils::AtomicInt::loadAcquire( isCancelled ) ) + return; + + // auto article_address = offsets.at( i ); + dict.getArticleText( offset, headword, articleText ); + articleText = articleText.normalized( QString::NormalizationForm_C ); + + if( ignoreDiacritics ) + articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); + + if( articleText.contains( searchRegularExpression ) ) + { + if( headword.isEmpty() ) + offsetsForHeadwords.append( offset ); + else + foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + + ++results; + if( maxResults > 0 && results >= maxResults ) + return; + } + } + } + else + { + // Words mode + + QVector< QPair< QString, bool > > wordsList; + if( ignoreWordsOrder ) + { + for( QStringList::const_iterator it = words.begin(); it != words.end(); ++it ) + wordsList.append( QPair< QString, bool >( *it, true ) ); + } + + // for( int i = 0; i < offsets.size(); i++ ) + { + if( Utils::AtomicInt::loadAcquire( isCancelled ) ) + return; + + if( ignoreWordsOrder ) + { + for( int i = 0; i < wordsList.size(); i++ ) + wordsList[ i ].second = true; + } + + dict.getArticleText( offset, headword, articleText ); + + articleText = articleText.normalized( QString::NormalizationForm_C ); + + if( ignoreDiacritics ) + articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); + + if( ignoreWordsOrder ) + { + bool allMatch = true; + foreach( QString word, words ) + { + if( containCJK( word ) || searchMode == FTS::PlainText ) + { + if( !articleText.contains( word ) ) + { + allMatch = false; + break; + } + } + else if( searchMode == FTS::WholeWords ) + { + QRegularExpression tmpReg( QString( "\b%1\b" ).arg( word ), + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::UseUnicodePropertiesOption ); + if( !articleText.contains( tmpReg ) ) + { + allMatch = false; + break; + } + } + } + + if( !allMatch ) + { + return; + } + + if( distanceBetweenWords >= 0 ) + { + // the article text contains all the needed words. + // determine if distance restriction is meet + QRegularExpression replaceReg( QString( "(%1)" ).arg( words.join( '|' ) ), + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::UseUnicodePropertiesOption ); + // use a string that could not be presented in the article. + articleText = articleText.replace( replaceReg, "=@XXXXX@=" ); + + auto hasCJK = false; + foreach( QString word, words ) + { + if( containCJK( word ) ) + { + hasCJK = true; + break; + } + } + + // hascjk value ,perhaps should depend on each word + auto searchRegStr = makeHiliteRegExpString( Utils::repeat( "=@XXXXX@=", words.size() ), + searchMode, + distanceBetweenWords, + hasCJK ); + QRegularExpression distanceOrderReg( searchRegStr, + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::UseUnicodePropertiesOption ); + // use a string that could not be presented in the article. + if( articleText.contains( distanceOrderReg ) ) + { + if( headword.isEmpty() ) + offsetsForHeadwords.append( offset ); + else + foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + + ++results; + if( maxResults > 0 && results >= maxResults ) + return; + } + } + } + else + { + if( articleText.contains( searchRegularExpression ) ) + { + if( headword.isEmpty() ) + offsetsForHeadwords.append( offset ); + else + foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + + ++results; + if( maxResults > 0 && results >= maxResults ) + return; + } + } + } + } + if( !offsetsForHeadwords.isEmpty() ) + { + QVector< QString > headwords; + dict.getHeadwordsFromOffsets( offsetsForHeadwords, headwords, &isCancelled ); + for( int x = 0; x < headwords.size(); x++ ) + foundHeadwords->append( FTS::FtsHeadword( headwords.at( x ), + id, + x < hiliteRegExps.size() ? hiliteRegExps.at( x ) : QStringList(), + matchCase ) ); + } +} + void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, sptr< ChunkedStorage::Reader > chunks, QStringList & indexWords, @@ -706,7 +896,8 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, dict.sortArticlesOffsetsForFTS( offsets, isCancelled ); - checkArticles( offsets, searchWords, regexp ); + // checkArticles( offsets, searchWords, regexp ); + QtConcurrent::blockingMapped(offsets,[&](uint32_t offset){checkSingleArticle(offset,searchWords,regexp); return 0;}); } void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, diff --git a/ftshelpers.hh b/ftshelpers.hh index e5c712b9..4bd309ac 100644 --- a/ftshelpers.hh +++ b/ftshelpers.hh @@ -82,12 +82,16 @@ class FTSResultsRequest : public Dictionary::DataRequest QAtomicInt isCancelled; + QAtomicInt results; + QList< FTS::FtsHeadword > * foundHeadwords; void checkArticles( QVector< uint32_t > const & offsets, QStringList const & words, QRegExp const & searchRegexp = QRegExp() ); + void checkSingleArticle( uint32_t offset, QStringList const & words, QRegExp const & searchRegexp = QRegExp() ); + void indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, sptr< ChunkedStorage::Reader > chunks, QStringList & indexWords, @@ -127,6 +131,7 @@ public: searchString = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( searchString_ ) ) ); foundHeadwords = new QList< FTS::FtsHeadword >; + results = 0; QThreadPool::globalInstance()->start( [ this ]() { this->run(); }, -100 ); } diff --git a/goldendict.pro b/goldendict.pro index 440dda9a..7ab55f53 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -47,7 +47,8 @@ QT += core \ webenginewidgets\ webchannel\ printsupport \ - help + help \ + concurrent greaterThan(QT_MAJOR_VERSION, 5): QT += webenginecore core5compat @@ -242,6 +243,7 @@ HEADERS += folding.hh \ ankiconnector.h \ article_inspect.h \ articlewebpage.h \ + base/globalregex.hh \ globalbroadcaster.h \ iframeschemehandler.h \ inc_case_folding.hh \ @@ -384,6 +386,7 @@ SOURCES += folding.cc \ ankiconnector.cpp \ article_inspect.cpp \ articlewebpage.cpp \ + base/globalregex.cc \ globalbroadcaster.cpp \ iframeschemehandler.cpp \ main.cc \ From 6c82bf71b6110bc0dfac49ac0d6cfc16b7e9abea Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 5 Jun 2022 10:44:40 +0800 Subject: [PATCH 083/125] improve: move temporary to global static --- base/globalregex.cc | 50 +++++++++++++++++++++++++++++ base/globalregex.hh | 40 +++++++++++++++++++++++ ftshelpers.cc | 78 ++++++++++++++++++--------------------------- mdx.cc | 76 ++++++++----------------------------------- 4 files changed, 135 insertions(+), 109 deletions(-) create mode 100644 base/globalregex.cc create mode 100644 base/globalregex.hh diff --git a/base/globalregex.cc b/base/globalregex.cc new file mode 100644 index 00000000..fab12fe5 --- /dev/null +++ b/base/globalregex.cc @@ -0,0 +1,50 @@ +#include "globalregex.hh" +#include "fulltextsearch.hh" + +using namespace RX; + +QRegularExpression Ftx::regBrackets( + "(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+)(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+){0,1}(\\([\\w\\p{M}]+\\)){0,1}", + QRegularExpression::UseUnicodePropertiesOption ); +QRegularExpression Ftx::regSplit( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); + +QRegularExpression Ftx::spacesRegExp( "\\W+", QRegularExpression::UseUnicodePropertiesOption ); +QRegularExpression Ftx::wordRegExp( QString( "\\w{" ) + QString::number( FTS::MinimumWordSize ) + ",}", + QRegularExpression::UseUnicodePropertiesOption ); +QRegularExpression Ftx::setsRegExp( "\\[[^\\]]+\\]", QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Ftx::regexRegExp( "\\\\[afnrtvdDwWsSbB]|\\\\x([0-9A-Fa-f]{4})|\\\\0([0-7]{3})", + QRegularExpression::CaseInsensitiveOption ); + + +//mdx + +QRegularExpression Mdx::allLinksRe( "(?:<\\s*(a(?:rea)?|img|link|script|source)(?:\\s+[^>]+|\\s*)>)", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::wordCrossLink( "([\\s\"']href\\s*=)\\s*([\"'])entry://([^>#]*?)((?:#[^>]*?)?)\\2", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::anchorIdRe( "([\\s\"'](?:name|id)\\s*=)\\s*([\"'])\\s*(?=\\S)", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::anchorIdReWord( "([\\s\"'](?:name|id)\\s*=)\\s*([\"'])\\s*(?=\\S)([^\"]*)", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::anchorIdRe2( "([\\s\"'](?:name|id)\\s*=)\\s*(?=[^\"'])([^\\s\">]+)", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::anchorLinkRe( "([\\s\"']href\\s*=\\s*[\"'])entry://#", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::audioRe( "([\\s\"']href\\s*=)\\s*([\"'])sound://([^\">]+)\\2", + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::InvertedGreedinessOption ); +QRegularExpression Mdx::stylesRe( "([\\s\"']href\\s*=)\\s*([\"'])(?!\\s*\\b(?:(?:bres|https?|ftp)://" + "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\">]+)\\2", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::stylesRe2( "([\\s\"']href\\s*=)\\s*(?![\\s\"']|\\b(?:(?:bres|https?|ftp)://" + "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\\s\">]+)", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::inlineScriptRe( "<\\s*script(?:(?=\\s)(?:(?![\\s\"']src\\s*=)[^>])+|\\s*)>", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::closeScriptTagRe( "<\\s*/script\\s*>", QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::srcRe( "([\\s\"']src\\s*=)\\s*([\"'])(?!\\s*\\b(?:(?:bres|https?|ftp)://" + "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\">]+)\\2", + QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Mdx::srcRe2( "([\\s\"']src\\s*=)\\s*(?![\\s\"']|\\b(?:(?:bres|https?|ftp)://" + "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\\s\">]+)", + QRegularExpression::CaseInsensitiveOption ); diff --git a/base/globalregex.hh b/base/globalregex.hh new file mode 100644 index 00000000..012aba0d --- /dev/null +++ b/base/globalregex.hh @@ -0,0 +1,40 @@ +#ifndef GLOBALREGEX_HH +#define GLOBALREGEX_HH + +#include + +namespace RX +{ +class Ftx +{ +public: + static QRegularExpression regBrackets; + static QRegularExpression regSplit; + static QRegularExpression spacesRegExp; + static QRegularExpression wordRegExp; + static QRegularExpression setsRegExp; + static QRegularExpression regexRegExp; +}; + + +class Mdx +{ +public: + static QRegularExpression allLinksRe; + static QRegularExpression wordCrossLink; + static QRegularExpression anchorIdRe; + static QRegularExpression anchorIdReWord; + static QRegularExpression anchorIdRe2; + static QRegularExpression anchorLinkRe; + static QRegularExpression audioRe; + static QRegularExpression stylesRe; + static QRegularExpression stylesRe2; + static QRegularExpression inlineScriptRe; + static QRegularExpression closeScriptTagRe; + static QRegularExpression srcRe; + static QRegularExpression srcRe2; +}; + +} // namespace RX + +#endif // GLOBALREGEX_HH diff --git a/ftshelpers.cc b/ftshelpers.cc index 65d0bc1a..6b3942e3 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -18,6 +18,7 @@ #include "wildcard.hh" #include +#include "base/globalregex.hh" using std::vector; using std::string; @@ -148,36 +149,36 @@ bool parseSearchString( QString const & str, QStringList & indexWords, { searchWords.clear(); indexWords.clear(); - QRegularExpression spacesRegExp( "\\W+", QRegularExpression::UseUnicodePropertiesOption ); - QRegularExpression wordRegExp( QString( "\\w{" ) + QString::number( FTS::MinimumWordSize ) + ",}", QRegularExpression::UseUnicodePropertiesOption ); - QRegularExpression setsRegExp( "\\[[^\\]]+\\]", QRegularExpression::CaseInsensitiveOption ); - QRegularExpression regexRegExp( "\\\\[afnrtvdDwWsSbB]|\\\\x([0-9A-Fa-f]{4})|\\\\0([0-7]{3})", QRegularExpression::CaseInsensitiveOption); + // QRegularExpression spacesRegExp( "\\W+", QRegularExpression::UseUnicodePropertiesOption ); + // QRegularExpression wordRegExp( QString( "\\w{" ) + QString::number( FTS::MinimumWordSize ) + ",}", QRegularExpression::UseUnicodePropertiesOption ); + // QRegularExpression setsRegExp( "\\[[^\\]]+\\]", QRegularExpression::CaseInsensitiveOption ); + // QRegularExpression regexRegExp( "\\\\[afnrtvdDwWsSbB]|\\\\x([0-9A-Fa-f]{4})|\\\\0([0-7]{3})", QRegularExpression::CaseInsensitiveOption); hasCJK = containCJK( str ); if( searchMode == FTS::WholeWords || searchMode == FTS::PlainText ) { // Make words list for search in article text - searchWords = str.normalized( QString::NormalizationForm_C ).split( spacesRegExp, Qt::SkipEmptyParts ); + searchWords = str.normalized( QString::NormalizationForm_C ).split( RX::Ftx::spacesRegExp, Qt::SkipEmptyParts ); // Make words list for index search QStringList list = - str.normalized( QString::NormalizationForm_C ).toLower().split( spacesRegExp, Qt::SkipEmptyParts ); + str.normalized( QString::NormalizationForm_C ).toLower().split( RX::Ftx::spacesRegExp, Qt::SkipEmptyParts ); QString searchString; if( hasCJK ) { - tokenizeCJK( indexWords, wordRegExp, list ); + tokenizeCJK( indexWords, RX::Ftx::wordRegExp, list ); // QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); searchString = makeHiliteRegExpString( list, searchMode, distanceBetweenWords, hasCJK , ignoreWordsOrder); } else { - indexWords = list.filter( wordRegExp ); + indexWords = list.filter( RX::Ftx::wordRegExp ); indexWords.removeDuplicates(); // Make regexp for results hilite - QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); + QStringList allWords = str.split( RX::Ftx::spacesRegExp, Qt::SkipEmptyParts ); searchString = makeHiliteRegExpString( allWords, searchMode, distanceBetweenWords,false, ignoreWordsOrder ); } searchRegExp = QRegExp( searchString, matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp2 ); @@ -192,21 +193,21 @@ bool parseSearchString( QString const & str, QStringList & indexWords, // Remove RegExp commands if( searchMode == FTS::RegExp ) - tmp.replace( regexRegExp, " " ); + tmp.replace( RX::Ftx::regexRegExp, " " ); // Remove all symbol sets - tmp.replace( setsRegExp, " " ); + tmp.replace( RX::Ftx::setsRegExp, " " ); QStringList list = tmp.normalized( QString::NormalizationForm_C ) - .toLower().split( spacesRegExp, Qt::SkipEmptyParts ); + .toLower().split( RX::Ftx::spacesRegExp, Qt::SkipEmptyParts ); if( hasCJK ) { - tokenizeCJK( indexWords, wordRegExp, list ); + tokenizeCJK( indexWords, RX::Ftx::wordRegExp, list ); } else { - indexWords = list.filter( wordRegExp ); + indexWords = list.filter( RX::Ftx::wordRegExp ); indexWords.removeDuplicates(); } @@ -225,9 +226,9 @@ void parseArticleForFts( uint32_t articleAddress, QString & articleText, if( articleText.isEmpty() ) return; - QRegularExpression regBrackets( "(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+)(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+){0,1}(\\([\\w\\p{M}]+\\)){0,1}", - QRegularExpression::UseUnicodePropertiesOption); - QRegularExpression regSplit( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); + // QRegularExpression regBrackets( "(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+)(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+){0,1}(\\([\\w\\p{M}]+\\)){0,1}", + // QRegularExpression::UseUnicodePropertiesOption); + // QRegularExpression regSplit( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); QStringList articleWords = articleText.normalized( QString::NormalizationForm_C ) .split( QRegularExpression( handleRoundBrackets ? "[^\\w\\(\\)\\p{M}]+" : "[^\\w\\p{M}]+", @@ -276,12 +277,12 @@ void parseArticleForFts( uint32_t articleAddress, QString & articleText, // Special handle for words with round brackets - DSL feature QStringList list; - QStringList oldVariant = word.split( regSplit, Qt::SkipEmptyParts ); + QStringList oldVariant = word.split( RX::Ftx::regSplit, Qt::SkipEmptyParts ); for( QStringList::iterator it = oldVariant.begin(); it != oldVariant.end(); ++it ) if( it->size() >= FTS::MinimumWordSize && !list.contains( *it ) ) list.append( *it ); - QRegularExpressionMatch match = regBrackets.match( word ); + QRegularExpressionMatch match = RX::Ftx::regBrackets.match( word ); if( match.hasMatch() ) { QStringList parts = match.capturedTexts(); @@ -452,15 +453,6 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QVector< QStringList > hiliteRegExps; QString id = QString::fromUtf8( dict.getId().c_str() ); - bool needHandleBrackets; - { - QString name = QString::fromUtf8( dict.getDictionaryFilenames()[ 0 ].c_str() ).toLower(); - needHandleBrackets = name.endsWith( ".dsl" ) || name.endsWith( ".dsl.dz" ); - } - - QRegularExpression regBrackets( "(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+)(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+){0,1}(\\([\\w\\p{M}]+\\)){0,1}", - QRegularExpression::UseUnicodePropertiesOption); - QRegularExpression regSplit( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); // RegExp mode QRegularExpression searchRegularExpression; @@ -507,9 +499,6 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, { // Words mode - QRegularExpression splitWithBrackets( "[^\\w\\(\\)\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); - QRegularExpression splitWithoutBrackets( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); - Qt::CaseSensitivity cs = matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive; QVector< QPair< QString, bool > > wordsList; if( ignoreWordsOrder ) @@ -602,7 +591,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, else foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); - results++; + ++results; if( maxResults > 0 && results >= maxResults ) break; } @@ -645,11 +634,6 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, QVector< QStringList > hiliteRegExps; QString id = QString::fromUtf8( dict.getId().c_str() ); - bool needHandleBrackets; - { - QString name = QString::fromUtf8( dict.getDictionaryFilenames()[ 0 ].c_str() ).toLower(); - needHandleBrackets = name.endsWith( ".dsl" ) || name.endsWith( ".dsl.dz" ); - } // RegExp mode QRegularExpression searchRegularExpression; @@ -757,9 +741,9 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, { // the article text contains all the needed words. // determine if distance restriction is meet - QRegularExpression replaceReg( QString( "(%1)" ).arg( words.join( '|' ) ), - QRegularExpression::CaseInsensitiveOption - | QRegularExpression::UseUnicodePropertiesOption ); + const QRegularExpression replaceReg( QString( "(%1)" ).arg( words.join( '|' ) ), + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::UseUnicodePropertiesOption ); // use a string that could not be presented in the article. articleText = articleText.replace( replaceReg, "=@XXXXX@=" ); @@ -774,13 +758,13 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, } // hascjk value ,perhaps should depend on each word - auto searchRegStr = makeHiliteRegExpString( Utils::repeat( "=@XXXXX@=", words.size() ), - searchMode, - distanceBetweenWords, - hasCJK ); - QRegularExpression distanceOrderReg( searchRegStr, - QRegularExpression::CaseInsensitiveOption - | QRegularExpression::UseUnicodePropertiesOption ); + const auto searchRegStr = makeHiliteRegExpString( Utils::repeat( "=@XXXXX@=", words.size() ), + searchMode, + distanceBetweenWords, + hasCJK ); + const QRegularExpression distanceOrderReg( searchRegStr, + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::UseUnicodePropertiesOption ); // use a string that could not be presented in the article. if( articleText.contains( distanceOrderReg ) ) { diff --git a/mdx.cc b/mdx.cc index caa3ec0f..fdada233 100644 --- a/mdx.cc +++ b/mdx.cc @@ -42,6 +42,7 @@ #include "tiff.hh" #include "utils.hh" +#include "base/globalregex.hh" namespace Mdx { @@ -192,51 +193,6 @@ public: }; -struct MdxRegex -{ - MdxRegex() : - allLinksRe( "(?:<\\s*(a(?:rea)?|img|link|script|source)(?:\\s+[^>]+|\\s*)>)", - QRegularExpression::CaseInsensitiveOption ), - wordCrossLink( "([\\s\"']href\\s*=)\\s*([\"'])entry://([^>#]*?)((?:#[^>]*?)?)\\2", - QRegularExpression::CaseInsensitiveOption ), - anchorIdRe( "([\\s\"'](?:name|id)\\s*=)\\s*([\"'])\\s*(?=\\S)", QRegularExpression::CaseInsensitiveOption ), - anchorIdReWord( "([\\s\"'](?:name|id)\\s*=)\\s*([\"'])\\s*(?=\\S)([^\"]*)", QRegularExpression::CaseInsensitiveOption ), - anchorIdRe2( "([\\s\"'](?:name|id)\\s*=)\\s*(?=[^\"'])([^\\s\">]+)", QRegularExpression::CaseInsensitiveOption ), - anchorLinkRe( "([\\s\"']href\\s*=\\s*[\"'])entry://#", QRegularExpression::CaseInsensitiveOption ), - audioRe( "([\\s\"']href\\s*=)\\s*([\"'])sound://([^\">]+)\\2", - QRegularExpression::CaseInsensitiveOption | QRegularExpression::InvertedGreedinessOption ), - stylesRe( "([\\s\"']href\\s*=)\\s*([\"'])(?!\\s*\\b(?:(?:bres|https?|ftp)://" - "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\">]+)\\2", - QRegularExpression::CaseInsensitiveOption ), - stylesRe2( "([\\s\"']href\\s*=)\\s*(?![\\s\"']|\\b(?:(?:bres|https?|ftp)://" - "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\\s\">]+)", - QRegularExpression::CaseInsensitiveOption ), - inlineScriptRe( "<\\s*script(?:(?=\\s)(?:(?![\\s\"']src\\s*=)[^>])+|\\s*)>", - QRegularExpression::CaseInsensitiveOption ), - closeScriptTagRe( "<\\s*/script\\s*>", QRegularExpression::CaseInsensitiveOption ), - srcRe( "([\\s\"']src\\s*=)\\s*([\"'])(?!\\s*\\b(?:(?:bres|https?|ftp)://" - "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\">]+)\\2", - QRegularExpression::CaseInsensitiveOption ), - srcRe2( "([\\s\"']src\\s*=)\\s*(?![\\s\"']|\\b(?:(?:bres|https?|ftp)://" - "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\\s\">]+)", - QRegularExpression::CaseInsensitiveOption ) - { - } - QRegularExpression allLinksRe; - QRegularExpression wordCrossLink; - QRegularExpression anchorIdRe; - QRegularExpression anchorIdReWord; - QRegularExpression anchorIdRe2; - QRegularExpression anchorLinkRe; - QRegularExpression audioRe; - QRegularExpression stylesRe; - QRegularExpression stylesRe2; - QRegularExpression inlineScriptRe; - QRegularExpression closeScriptTagRe; - QRegularExpression srcRe; - QRegularExpression srcRe2; -}; - class MdxDictionary: public BtreeIndexing::BtreeDictionary { Mutex idxMutex; @@ -256,8 +212,6 @@ class MdxDictionary: public BtreeIndexing::BtreeDictionary string initError; QString cacheDirName; - static MdxRegex mdxRx; - public: MdxDictionary( string const & id, string const & indexFile, vector const & dictionaryFiles ); @@ -347,8 +301,6 @@ private: friend class MddResourceRequest; }; -MdxRegex MdxDictionary::mdxRx; - MdxDictionary::MdxDictionary( string const & id, string const & indexFile, vector const & dictionaryFiles ): BtreeDictionary( id, dictionaryFiles ), @@ -987,7 +939,7 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar QString articleNewText; int linkPos = 0; - QRegularExpressionMatchIterator it = mdxRx.allLinksRe.globalMatch( article ); + QRegularExpressionMatchIterator it = RX::Mdx::allLinksRe.globalMatch( article ); QMap idMap; while( it.hasNext() ) { @@ -1005,10 +957,10 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar if( !linkType.isEmpty() && linkType.at( 0 ) == 'a' ) { - QRegularExpressionMatch match = mdxRx.anchorIdRe.match( linkTxt ); + QRegularExpressionMatch match = RX::Mdx::anchorIdRe.match( linkTxt ); if( match.hasMatch() ) { - auto wordMatch = mdxRx.anchorIdReWord.match( linkTxt ); + auto wordMatch = RX::Mdx::anchorIdReWord.match( linkTxt ); if( wordMatch.hasMatch() ) { idMap.insert( wordMatch.captured( 3 ), uniquePrefix + wordMatch.captured( 3 ) ); @@ -1017,11 +969,11 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar newLink = linkTxt.replace( match.capturedStart(), match.capturedLength(), newText ); } else - newLink = linkTxt.replace( mdxRx.anchorIdRe2, "\\1\"" + uniquePrefix + "\\2\"" ); + newLink = linkTxt.replace( RX::Mdx::anchorIdRe2, "\\1\"" + uniquePrefix + "\\2\"" ); - newLink = newLink.replace( mdxRx.anchorLinkRe, "\\1#" + uniquePrefix ); + newLink = newLink.replace( RX::Mdx::anchorLinkRe, "\\1#" + uniquePrefix ); - match = mdxRx.audioRe.match( newLink ); + match = RX::Mdx::audioRe.match( newLink ); if( match.hasMatch() ) { // sounds and audio link script @@ -1032,7 +984,7 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar + newLink.replace( match.capturedStart(), match.capturedLength(), newTxt ); } - match = mdxRx.wordCrossLink.match( newLink ); + match = RX::Mdx::wordCrossLink.match( newLink ); if( match.hasMatch() ) { QString newTxt = match.captured( 1 ) + match.captured( 2 ) @@ -1050,7 +1002,7 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar if( linkType.compare( "link" ) == 0 ) { // stylesheets - QRegularExpressionMatch match = mdxRx.stylesRe.match( linkTxt ); + QRegularExpressionMatch match = RX::Mdx::stylesRe.match( linkTxt ); if( match.hasMatch() ) { QString newText = match.captured( 1 ) + match.captured( 2 ) @@ -1059,7 +1011,7 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar newLink = linkTxt.replace( match.capturedStart(), match.capturedLength(), newText ); } else - newLink = linkTxt.replace( mdxRx.stylesRe2, + newLink = linkTxt.replace( RX::Mdx::stylesRe2, "\\1\"bres://" + id + "/\\2\"" ); } else @@ -1067,13 +1019,13 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar || linkType.compare( "source" ) == 0 ) { // javascripts and images - QRegularExpressionMatch match = mdxRx.inlineScriptRe.match( linkTxt ); + QRegularExpressionMatch match = RX::Mdx::inlineScriptRe.match( linkTxt ); if( linkType.at( 1 ) == 'c' // "script" tag && match.hasMatch() && match.capturedLength() == linkTxt.length() ) { // skip inline scripts articleNewText += linkTxt; - match = mdxRx.closeScriptTagRe.match( article, linkPos ); + match = RX::Mdx::closeScriptTagRe.match( article, linkPos ); if( match.hasMatch() ) { articleNewText += article.mid( linkPos, match.capturedEnd() - linkPos ); @@ -1083,7 +1035,7 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar } else { - match = mdxRx.srcRe.match( linkTxt ); + match = RX::Mdx::srcRe.match( linkTxt ); if( match.hasMatch() ) { QString newText; @@ -1104,7 +1056,7 @@ QString & MdxDictionary::filterResource( QString const & articleId, QString & ar newLink = linkTxt.replace( match.capturedStart(), match.capturedLength(), newText ); } else - newLink = linkTxt.replace( mdxRx.srcRe2, + newLink = linkTxt.replace( RX::Mdx::srcRe2, "\\1\"bres://" + id + "/\\2\"" ); } } From 33b3a95e347c9d56b0e1bf636d3d3475a1da5166 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 5 Jun 2022 12:48:52 +0800 Subject: [PATCH 084/125] add qt5.15.2 compatibility with qtconcurrent --- ftshelpers.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 6b3942e3..788ac757 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -880,8 +880,11 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, dict.sortArticlesOffsetsForFTS( offsets, isCancelled ); - // checkArticles( offsets, searchWords, regexp ); - QtConcurrent::blockingMapped(offsets,[&](uint32_t offset){checkSingleArticle(offset,searchWords,regexp); return 0;}); +//#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + QtConcurrent::blockingMap(offsets,[&](uint32_t offset){checkSingleArticle(offset,searchWords,regexp);}); +//#else +// checkArticles( offsets, searchWords, regexp ); +//#endif } void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, From 886aa7f25dfa1463237f3ea0cf02cf126c1ba772 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 5 Jun 2022 13:11:01 +0800 Subject: [PATCH 085/125] github: PR check add concurrency restriction --- .github/workflows/macos-PR-check.yml | 4 +++- .github/workflows/ubuntu-PR-check.yml | 4 +++- .github/workflows/windows-PR-check.yml | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos-PR-check.yml b/.github/workflows/macos-PR-check.yml index def35f86..3fb902b7 100644 --- a/.github/workflows/macos-PR-check.yml +++ b/.github/workflows/macos-PR-check.yml @@ -1,5 +1,7 @@ name: macos-PR-check - +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: workflow_dispatch: diff --git a/.github/workflows/ubuntu-PR-check.yml b/.github/workflows/ubuntu-PR-check.yml index 5fad1eb0..781c5225 100644 --- a/.github/workflows/ubuntu-PR-check.yml +++ b/.github/workflows/ubuntu-PR-check.yml @@ -1,5 +1,7 @@ name: Ubuntu-PR-check - +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: workflow_dispatch: diff --git a/.github/workflows/windows-PR-check.yml b/.github/workflows/windows-PR-check.yml index ea7348bb..9a0724d6 100644 --- a/.github/workflows/windows-PR-check.yml +++ b/.github/workflows/windows-PR-check.yml @@ -1,5 +1,7 @@ name: Windows-PR-check - +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: workflow_dispatch: From 76969aa49d760ea5aa72efac2a9b7c1e6841cb3d Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 5 Jun 2022 16:31:44 +0800 Subject: [PATCH 086/125] fulltext search add concurrent support --- ftshelpers.cc | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 788ac757..e3b3ae41 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -814,27 +814,26 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, { // Find articles which contains all requested words - vector< BtreeIndexing::WordArticleLink > links; - QSet< uint32_t > setOfOffsets, tmp; - uint32_t size; + QSet< uint32_t > setOfOffsets; if( indexWords.isEmpty() ) return; - int n = indexWords.length(); - for( int i = 0; i < n; i++ ) + auto findLinks = [ & ]( const QString & word ) -> QSet< uint32_t > { + QSet< uint32_t > tmp; + uint32_t size; + if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - return; + return tmp; - tmp.clear(); - - links = ftsIndex.findArticles( gd::toWString( indexWords.at( i ) ), ignoreDiacritics ); + vector< BtreeIndexing::WordArticleLink > links = + ftsIndex.findArticles( gd::toWString( word ), ignoreDiacritics ); for( unsigned x = 0; x < links.size(); x++ ) { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - return; + return tmp; vector< char > chunk; char * linksPtr; @@ -843,24 +842,32 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, linksPtr = chunks->getBlock( links[ x ].articleOffset, chunk ); } - memcpy( &size, linksPtr, sizeof(uint32_t) ); - linksPtr += sizeof(uint32_t); + memcpy( &size, linksPtr, sizeof( uint32_t ) ); + linksPtr += sizeof( uint32_t ); for( uint32_t y = 0; y < size; y++ ) { tmp.insert( *( reinterpret_cast< uint32_t * >( linksPtr ) ) ); - linksPtr += sizeof(uint32_t); + linksPtr += sizeof( uint32_t ); } } links.clear(); - if( i == 0 ) - setOfOffsets = tmp; + return tmp; + }; + // int n = indexWords.length(); + auto sets = QtConcurrent::blockingMapped( indexWords, + findLinks ); + + int i = 0; + for( auto & elem : sets ) + { + if( i++ == 0 ) + setOfOffsets = elem; else - setOfOffsets = setOfOffsets.intersect( tmp ); + setOfOffsets = setOfOffsets.intersect( elem ); } - tmp.clear(); if( setOfOffsets.isEmpty() ) return; From 837dcfbf63db75c1e22243a483e3d6e4bf524187 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 8 Jun 2022 21:38:30 +0800 Subject: [PATCH 087/125] qt5.15.2 does not work with qtconcurrent::blockingmapped --- ftshelpers.cc | 220 ++++++-------------------------------------------- 1 file changed, 24 insertions(+), 196 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index e3b3ae41..997c05c0 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -447,180 +447,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QStringList const & words, QRegExp const & searchRegexp ) { - // int results = 0; - QString headword, articleText; - QList< uint32_t > offsetsForHeadwords; - QVector< QStringList > hiliteRegExps; - - QString id = QString::fromUtf8( dict.getId().c_str() ); - - // RegExp mode - QRegularExpression searchRegularExpression; - if( searchMode == FTS::Wildcards ) - searchRegularExpression.setPattern( wildcardsToRegexp( searchRegexp.pattern() ) ); - else - searchRegularExpression.setPattern( searchRegexp.pattern() ); - QRegularExpression::PatternOptions patternOptions = - QRegularExpression::DotMatchesEverythingOption | QRegularExpression::UseUnicodePropertiesOption - | QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption; - if( searchRegexp.caseSensitivity() == Qt::CaseInsensitive ) - patternOptions |= QRegularExpression::CaseInsensitiveOption; - searchRegularExpression.setPatternOptions( patternOptions ); - if( !searchRegularExpression.isValid() ) - searchRegularExpression.setPattern( "" ); - - if( searchMode == FTS::Wildcards || searchMode == FTS::RegExp ) - { - for( int i = 0; i < offsets.size(); i++ ) - { - if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - break; - - dict.getArticleText( offsets.at( i ), headword, articleText ); - articleText = articleText.normalized( QString::NormalizationForm_C ); - - if( ignoreDiacritics ) - articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); - - if( articleText.contains( searchRegularExpression ) ) - { - if( headword.isEmpty() ) - offsetsForHeadwords.append( offsets.at( i ) ); - else - foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); - - results++; - if( maxResults > 0 && results >= maxResults ) - break; - } - } - } - else - { - // Words mode - - Qt::CaseSensitivity cs = matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive; - QVector< QPair< QString, bool > > wordsList; - if( ignoreWordsOrder ) - { - for( QStringList::const_iterator it = words.begin(); it != words.end(); ++it ) - wordsList.append( QPair< QString, bool >( *it, true ) ); - } - - for( int i = 0; i < offsets.size(); i++ ) - { - if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - break; - - QVector< QStringList > allOrders; - QStringList order; - - if( ignoreWordsOrder ) - { - for( int i = 0; i < wordsList.size(); i++ ) - wordsList[ i ].second = true; - } - - dict.getArticleText( offsets.at( i ), headword, articleText ); - - articleText = articleText.normalized( QString::NormalizationForm_C ); - - if( ignoreDiacritics ) - articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); - - if(ignoreWordsOrder) - { - bool allMatch = true; - foreach( QString word, words ) - { - if( containCJK( word ) || searchMode == FTS::PlainText ) - { - if( !articleText.contains( word ) ) - { - allMatch = false; - break; - } - } - else if( searchMode == FTS::WholeWords) - { - QRegularExpression tmpReg( QString( "\b%1\b" ).arg( word ),QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption ); - if( !articleText.contains( tmpReg) ) - { - allMatch = false; - break; - } - } - - } - - if(!allMatch) - { - continue; - } - - if( distanceBetweenWords >= 0 ) - { - // the article text contains all the needed words. - // determine if distance restriction is meet - QRegularExpression replaceReg( QString( "(%1)" ).arg( words.join( '|' ) ), - QRegularExpression::CaseInsensitiveOption | - QRegularExpression::UseUnicodePropertiesOption ); - // use a string that could not be presented in the article. - articleText = articleText.replace( replaceReg, "=@XXXXX@=" ); - - auto hasCJK = false; - foreach(QString word,words) - { - if(containCJK( word )) - { - hasCJK = true; - break; - } - } - - //hascjk value ,perhaps should depend on each word - auto searchRegStr = makeHiliteRegExpString( Utils::repeat( "=@XXXXX@=", words.size() ), searchMode, distanceBetweenWords,hasCJK ); - QRegularExpression distanceOrderReg( searchRegStr, - QRegularExpression::CaseInsensitiveOption | - QRegularExpression::UseUnicodePropertiesOption ); - // use a string that could not be presented in the article. - if(articleText.contains(distanceOrderReg)) - { - if( headword.isEmpty() ) - offsetsForHeadwords.append( offsets.at( i ) ); - else - foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); - - ++results; - if( maxResults > 0 && results >= maxResults ) - break; - } - } - - } - else - { - if( articleText.contains( searchRegularExpression ) ) - { - if( headword.isEmpty() ) - offsetsForHeadwords.append( offsets.at( i ) ); - else - foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); - - ++results; - if( maxResults > 0 && results >= maxResults ) - break; - } - } - } - } - if( !offsetsForHeadwords.isEmpty() ) - { - QVector< QString > headwords; - dict.getHeadwordsFromOffsets( offsetsForHeadwords, headwords, &isCancelled ); - for( int x = 0; x < headwords.size(); x++ ) - foundHeadwords->append( FTS::FtsHeadword( headwords.at( x ), id, x < hiliteRegExps.size() ? hiliteRegExps.at( x ) : QStringList(), matchCase ) ); - } + QtConcurrent::blockingMap( offsets, [ & ]( uint32_t offset ) { checkSingleArticle( offset, words, searchRegexp ); } ); } void FTSResultsRequest::checkSingleArticle( uint32_t offset, @@ -819,13 +646,15 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, if( indexWords.isEmpty() ) return; - auto findLinks = [ & ]( const QString & word ) -> QSet< uint32_t > + QList< QSet< uint32_t > > addressLists; + + auto findLinks = [ & ]( const QString & word ) { QSet< uint32_t > tmp; uint32_t size; if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - return tmp; + addressLists<< tmp; vector< BtreeIndexing::WordArticleLink > links = ftsIndex.findArticles( gd::toWString( word ), ignoreDiacritics ); @@ -833,7 +662,7 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - return tmp; + addressLists<< tmp; vector< char > chunk; char * linksPtr; @@ -853,14 +682,13 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, links.clear(); - return tmp; + addressLists<< tmp; }; // int n = indexWords.length(); - auto sets = QtConcurrent::blockingMapped( indexWords, - findLinks ); + QtConcurrent::blockingMap( indexWords, findLinks ); int i = 0; - for( auto & elem : sets ) + for( auto & elem : addressLists ) { if( i++ == 0 ) setOfOffsets = elem; @@ -887,11 +715,7 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, dict.sortArticlesOffsetsForFTS( offsets, isCancelled ); -//#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) - QtConcurrent::blockingMap(offsets,[&](uint32_t offset){checkSingleArticle(offset,searchWords,regexp);}); -//#else -// checkArticles( offsets, searchWords, regexp ); -//#endif + checkArticles( offsets, searchWords, regexp ); } void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, @@ -934,17 +758,15 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde if( !hieroglyphsList.empty() ) { - QSet< uint32_t > tmp; - vector< BtreeIndexing::WordArticleLink > links; - - for( int i = 0; i < hieroglyphsList.size(); i++ ) + QList< QSet< uint32_t > > sets; + auto fn_wordLink = [ & ](const QString & word ) { - links = ftsIndex.findArticles( gd::toWString( hieroglyphsList.at( i ) ) ); + QSet< uint32_t > tmp; + vector< BtreeIndexing::WordArticleLink > links = ftsIndex.findArticles( gd::toWString( word ) ); for( unsigned x = 0; x < links.size(); x++ ) { - if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - return; + sets<< tmp; vector< char > chunk; char * linksPtr; @@ -963,11 +785,17 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde } links.clear(); + sets<< tmp; + }; + QtConcurrent::blockingMap( hieroglyphsList, fn_wordLink ); - if( i == 0 ) - setOfOffsets = tmp; + int i = 0; + for( auto & elem : sets ) + { + if( i++ == 0 ) + setOfOffsets = elem; else - setOfOffsets = setOfOffsets.intersect( tmp ); + setOfOffsets = setOfOffsets.intersect( elem ); } allWordsLinks[ wordNom ] = setOfOffsets; From 058ba392fd661c78d9915ff321fdc9e8774d7a27 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 11 Jun 2022 10:29:21 +0800 Subject: [PATCH 088/125] opt: fullindex need not to care about stylesheet --- mdx.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mdx.cc b/mdx.cc index fdada233..8998bfa5 100644 --- a/mdx.cc +++ b/mdx.cc @@ -924,10 +924,11 @@ void MdxDictionary::loadArticle( uint32_t offset, string & articleText, bool noF decompressed.constData() + recordInfo.recordOffset, recordInfo.recordSize ); - article = MdictParser::substituteStylesheet( article, styleSheets ); - if( !noFilter ) + { + article = MdictParser::substituteStylesheet( article, styleSheets ); article = filterResource( articleId, article ); + } articleText = article.toStdString(); } From ca0209faff1f2c759092ab8ca64d64122b4912a7 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sat, 11 Jun 2022 17:33:40 +0300 Subject: [PATCH 089/125] Save current article and window position before loading DSL picture Without the added saveHistoryUserData() call, returning back from the enlarged picture page ("gdpicture") restores the current article and the window position that were last saved. At this commit the "gdpicture" behavior is consistent with regular links: returning back from the enlarged picture page sets the article with the picture as current and restores the window position at the time of the click on the picture. --- articleview.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/articleview.cc b/articleview.cc index b0e2d1ae..52952c6f 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1197,7 +1197,10 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, Contexts contexts( contexts_ ); if( url.scheme().compare( "gdpicture" ) == 0 ) + { + saveHistoryUserData(); ui.definition->load( url ); + } else if ( url.scheme().compare( "bword" ) == 0 ) { From e8a1358bceeba1e20dfadf010eb489c6a2a53554 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sun, 12 Jun 2022 14:26:15 +0300 Subject: [PATCH 090/125] Refactor: extract ArticleView::load() This way it is clearer that the pages history is updated just before each navigation to a different page. The call to ArticleView::saveHistoryUserData() now occurs slightly later in ArticleView::showDefinition(). I don't think the intervening code can affect the current article or window position. So the reordering most likely does not affect application behavior. --- articleview.cc | 25 ++++++++++++------------- articleview.hh | 3 +++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/articleview.cc b/articleview.cc index 52952c6f..1b2b13cd 100644 --- a/articleview.cc +++ b/articleview.cc @@ -444,8 +444,7 @@ void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned g if ( mutedDicts.size() ) Qt4x5::Url::addQueryItem( req, "muted", mutedDicts ); - // Update both histories (pages history and headwords history) - saveHistoryUserData(); + // Update headwords history emit sendWordToHistory( phrase.phrase ); // Any search opened is probably irrelevant now @@ -456,7 +455,7 @@ void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned g emit setExpandMode( expandOptionalParts ); - ui.definition->load( req ); + load( req ); //QApplication::setOverrideCursor( Qt::WaitCursor ); ui.definition->setCursor( Qt::WaitCursor ); @@ -494,8 +493,7 @@ void ArticleView::showDefinition( QString const & word, QStringList const & dict if( ignoreDiacritics ) Qt4x5::Url::addQueryItem( req, "ignore_diacritics", "1" ); - // Update both histories (pages history and headwords history) - saveHistoryUserData(); + // Update headwords history emit sendWordToHistory( word ); // Any search opened is probably irrelevant now @@ -506,7 +504,7 @@ void ArticleView::showDefinition( QString const & word, QStringList const & dict emit setExpandMode( expandOptionalParts ); - ui.definition->load( req ); + load( req ); //QApplication::setOverrideCursor( Qt::WaitCursor ); ui.definition->setCursor( Qt::WaitCursor ); @@ -877,6 +875,12 @@ void ArticleView::saveHistoryUserData() ui.definition->history()->currentItem().setUserData( userData ); } +void ArticleView::load( QUrl const & url ) +{ + saveHistoryUserData(); + ui.definition->load( url ); +} + void ArticleView::cleanupTemp() { QSet< QString >::iterator it = desktopOpenedTempFiles.begin(); @@ -1197,10 +1201,7 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, Contexts contexts( contexts_ ); if( url.scheme().compare( "gdpicture" ) == 0 ) - { - saveHistoryUserData(); - ui.definition->load( url ); - } + load( url ); else if ( url.scheme().compare( "bword" ) == 0 ) { @@ -1648,9 +1649,7 @@ void ArticleView::updateMutedContents() if ( mutedDicts.size() ) Qt4x5::Url::addQueryItem( currentUrl, "muted", mutedDicts ); - saveHistoryUserData(); - - ui.definition->load( currentUrl ); + load( currentUrl ); //QApplication::setOverrideCursor( Qt::WaitCursor ); ui.definition->setCursor( Qt::WaitCursor ); diff --git a/articleview.hh b/articleview.hh index 484c2d5f..3f2b9804 100644 --- a/articleview.hh +++ b/articleview.hh @@ -346,6 +346,9 @@ private: /// Should be used when leaving the page. void saveHistoryUserData(); + /// Loads a page at @p url into view. + void load( QUrl const & url ); + /// Attempts removing last temporary file created. void cleanupTemp(); From de69036e9eaa5d1e1516f01e21a506eaf51b6f15 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sat, 11 Jun 2022 20:34:52 +0300 Subject: [PATCH 091/125] ArticleView: deduplicate expand-optional-parts code --- articleview.cc | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/articleview.cc b/articleview.cc index 1b2b13cd..eb7f2e04 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2516,15 +2516,7 @@ void ArticleView::showEvent( QShowEvent * ev ) void ArticleView::receiveExpandOptionalParts( bool expand ) { if( expandOptionalParts != expand ) - { - int n = getArticlesList().indexOf( getActiveArticleId() ); - if( n > 0 ) - articleToJump = getCurrentArticle(); - - emit setExpandMode( expand ); - expandOptionalParts = expand; - reload(); - } + switchExpandOptionalParts(); } void ArticleView::switchExpandOptionalParts() From 0173251b2277173f36eca36e9c2ce137caba3c52 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sat, 11 Jun 2022 20:37:50 +0300 Subject: [PATCH 092/125] Jump to the first article too after expanding optional parts When the first article in the list is current, expanding or collapsing optional parts results in: 1) uncontrolled jumps due to the content height changes if the scroll position is not (0, 0); 2) current article change if a non-first article is saved in history user data (e.g. if a mouse click had made the first article active). Treat the first current article in the same way as non-first ones. This change also affects the case when the current article is not present in the article list. If the current article is simply empty, then there is no behavior change. If the current article is not empty, it *must* be in the list, unless something else went wrong. --- articleview.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/articleview.cc b/articleview.cc index eb7f2e04..80d94913 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2522,11 +2522,7 @@ void ArticleView::receiveExpandOptionalParts( bool expand ) void ArticleView::switchExpandOptionalParts() { expandOptionalParts = !expandOptionalParts; - - int n = getArticlesList().indexOf( getActiveArticleId() ); - if( n > 0 ) - articleToJump = getCurrentArticle(); - + articleToJump = getCurrentArticle(); emit setExpandMode( expandOptionalParts ); reload(); } From 40dd01755e27e3ee4fa642421634d5374f929f30 Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sat, 11 Jun 2022 20:57:52 +0300 Subject: [PATCH 093/125] Always jump to current article before reloading MainWindow calls ArticleView::reload() when the group list is updated. This updating may add/remove/reorder dictionaries in the active group. MainWindow also calls ArticleView::reload() when the display or addon style changes. In both of the above scenarios uncontrolled jumps and current article change can occur (see also the parent commit message). Move setting articleToJump from above the only ArticleView's reload() call into ArticleView::reload() itself. --- articleview.cc | 7 ++++++- articleview.hh | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/articleview.cc b/articleview.cc index 80d94913..a324c5b9 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1690,6 +1690,12 @@ void ArticleView::forward() ui.definition->forward(); } +void ArticleView::reload() +{ + articleToJump = getCurrentArticle(); + ui.definition->reload(); +} + bool ArticleView::hasSound() { QVariant v = ui.definition->page()->mainFrame()->evaluateJavaScript( "gdAudioLinks.first" ); @@ -2522,7 +2528,6 @@ void ArticleView::receiveExpandOptionalParts( bool expand ) void ArticleView::switchExpandOptionalParts() { expandOptionalParts = !expandOptionalParts; - articleToJump = getCurrentArticle(); emit setExpandMode( expandOptionalParts ); reload(); } diff --git a/articleview.hh b/articleview.hh index 3f2b9804..6e3d0ebf 100644 --- a/articleview.hh +++ b/articleview.hh @@ -153,8 +153,7 @@ public slots: public: /// Reloads the view - void reload() - { ui.definition->reload(); } + void reload(); /// Returns true if there's an audio reference on the page, false otherwise. bool hasSound(); From 7df106c68c51eaed9678d955c27004dbdb188c6e Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sat, 11 Jun 2022 21:31:06 +0300 Subject: [PATCH 094/125] Refactor: get rid of ArticleView::articleToJump This variable overrides history user data, which makes the current article and position restoring code in ArticleView::loadFinished() difficult to understand. Encode the same logic in the history user data instead. This should make the code more straightforward and less brittle in the face of changes. --- articleview.cc | 96 ++++++++++++++++++++++++++++---------------------- articleview.hh | 1 - 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/articleview.cc b/articleview.cc index a324c5b9..aef75b76 100644 --- a/articleview.cc +++ b/articleview.cc @@ -564,54 +564,51 @@ void ArticleView::loadFinished( bool ) // Expand collapsed article if only one loaded ui.definition->page()->mainFrame()->evaluateJavaScript( "gdCheckArticlesNumber();" ); - bool jumpedToCurrentArticle = false; - // Jump to current article after page reloading - if( !articleToJump.isEmpty() ) + QVariant userDataVariant = ui.definition->history()->currentItem().userData(); + if ( userDataVariant.type() == QVariant::Map ) { - jumpedToCurrentArticle = setCurrentArticle( articleToJump, true ); - articleToJump.clear(); - } + QMap< QString, QVariant > userData = userDataVariant.toMap(); - if( !jumpedToCurrentArticle ) - { - QVariant userDataVariant = ui.definition->history()->currentItem().userData(); + double sx = 0, sy = 0; + bool moveToCurrentArticle = true; - if ( userDataVariant.type() == QVariant::Map ) + if ( userData.value( "sx" ).type() == QVariant::Double ) { - QMap< QString, QVariant > userData = userDataVariant.toMap(); - - QString currentArticle = userData.value( "currentArticle" ).toString(); - - if ( currentArticle.size() ) - { - // There's an active article saved, so set it to be active. - setCurrentArticle( currentArticle ); - } - - double sx = 0, sy = 0; - - if ( userData.value( "sx" ).type() == QVariant::Double ) - sx = userData.value( "sx" ).toDouble(); - - if ( userData.value( "sy" ).type() == QVariant::Double ) - sy = userData.value( "sy" ).toDouble(); - - if ( sx != 0 || sy != 0 ) - { - // Restore scroll position - ui.definition->page()->mainFrame()->evaluateJavaScript( - QString( "window.scroll( %1, %2 );" ).arg( sx ).arg( sy ) ); - } + sx = userData.value( "sx" ).toDouble(); + moveToCurrentArticle = false; } - else + + if ( userData.value( "sy" ).type() == QVariant::Double ) { - QString const scrollTo = Qt4x5::Url::queryItemValue( url, "scrollto" ); - if( isScrollTo( scrollTo ) ) - { - // There is no active article saved in history, but we have it as a parameter. - // setCurrentArticle will save it and scroll there. - setCurrentArticle( scrollTo, true ); - } + sy = userData.value( "sy" ).toDouble(); + moveToCurrentArticle = false; + } + + const QString currentArticle = userData.value( "currentArticle" ).toString(); + if( !currentArticle.isEmpty() ) + { + // There's a current article saved, so set it to be current. + // If a scroll position was stored - even (0, 0) - don't move to the + // current article. + setCurrentArticle( currentArticle, moveToCurrentArticle ); + } + + if ( sx != 0 || sy != 0 ) + { + // Restore scroll position if at least one non-zero coordinate was stored. + // Moving to (0, 0) is a no-op, so don't restore it. + ui.definition->page()->mainFrame()->evaluateJavaScript( + QString( "window.scroll( %1, %2 );" ).arg( sx ).arg( sy ) ); + } + } + else + { + QString const scrollTo = Qt4x5::Url::queryItemValue( url, "scrollto" ); + if( isScrollTo( scrollTo ) ) + { + // There is no active article saved in history, but we have it as a parameter. + // setCurrentArticle will save it and scroll there. + setCurrentArticle( scrollTo, true ); } } @@ -1692,7 +1689,20 @@ void ArticleView::forward() void ArticleView::reload() { - articleToJump = getCurrentArticle(); + QMap< QString, QVariant > userData = ui.definition->history()->currentItem().userData().toMap(); + + // Save current article, which can be empty + userData[ "currentArticle" ] = getCurrentArticle(); + + // Remove saved window position. Reloading occurs in response to changes that + // may affect content height, so restoring the current window position can cause + // uncontrolled jumps. Scrolling to the current article (i.e. jumping to the top + // of it) is simple, reliable and predictable, if not ideal. + userData[ "sx" ].clear(); + userData[ "sy" ].clear(); + + ui.definition->history()->currentItem().setUserData( userData ); + ui.definition->reload(); } diff --git a/articleview.hh b/articleview.hh index 6e3d0ebf..a9047aac 100644 --- a/articleview.hh +++ b/articleview.hh @@ -41,7 +41,6 @@ class ArticleView: public QFrame QAction & openSearchAction; bool searchIsOpened; bool expandOptionalParts; - QString articleToJump; QString rangeVarName; /// Any resource we've decided to download off the dictionary gets stored here. From 9b5756d5e156f7714fba6e21fffc9ca179df9a4e Mon Sep 17 00:00:00 2001 From: Igor Kushnir Date: Sun, 12 Jun 2022 20:59:09 +0300 Subject: [PATCH 095/125] Don't update pages history in ArticleView::setCurrentArticle() The current article is saved to pages history user data before any navigation to another page and before reloading the current page. Therefore saving it each time the user activates an article is redundant. This redundancy is not consistent, because when a user activates an article by clicking on it, the current article changes in the UI, but is not immediately saved in history as setCurrentArticle() is not called. The inconsistent redundancy is a waste of CPU time and can hide bugs. --- articleview.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/articleview.cc b/articleview.cc index aef75b76..c71cfa86 100644 --- a/articleview.cc +++ b/articleview.cc @@ -761,10 +761,6 @@ bool ArticleView::setCurrentArticle( QString const & id, bool moveToIt ) if ( moveToIt ) ui.definition->page()->mainFrame()->evaluateJavaScript( QString( "document.getElementById('%1').scrollIntoView(true);" ).arg( id ) ); - QMap< QString, QVariant > userData = ui.definition->history()->currentItem().userData().toMap(); - userData[ "currentArticle" ] = id; - ui.definition->history()->currentItem().setUserData( userData ); - ui.definition->page()->mainFrame()->evaluateJavaScript( QString( "gdMakeArticleActive( '%1' );" ).arg( dictionaryId ) ); From ec5d2b12db49edcba13463eca69e4d61b29d19be Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 11 Jun 2022 23:31:33 +0800 Subject: [PATCH 096/125] style:do not allow dictionary title to be selected --- article-style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/article-style.css b/article-style.css index 959edbaa..4f2f871e 100644 --- a/article-style.css +++ b/article-style.css @@ -42,6 +42,11 @@ pre /*background: #ffffdd;*/ } +.gddicttitle +{ + user-select: none; +} + .gddictnamebodyseparator { clear: both; From e8bcaa225266601afbd8e4ebc2bfd5e12dec4fac Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 11 Jun 2022 23:37:58 +0800 Subject: [PATCH 097/125] style: move duplicate class together --- article-style-st-lingvo.css | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/article-style-st-lingvo.css b/article-style-st-lingvo.css index 8da59179..20974124 100644 --- a/article-style-st-lingvo.css +++ b/article-style-st-lingvo.css @@ -24,24 +24,19 @@ a:hover background: white; } -/* Dictionary's name heading */ -.gddictname -{ - border: 1px dotted black; padding: 0.2em; padding-left: 0.5em; - margin-top: 1.2em; margin-bottom: 0.1em; font-weight: bold; font-size: 14px; - background: #87CEEB; -} - /* The 'From ' string which preceeds dictionary name in the heading */ .gdfromprefix { display: none; } +/* Dictionary's name heading */ .gddictname { + padding: 0.2em; padding-left: 0.5em; + margin-bottom: 0.1em; + font-size: 14px; font-weight: normal; - float: right; border: 1px solid white; margin-top: 7px; From ab0a8ed60875ad72e951dbbcec2deb33c449fd99 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sun, 12 Jun 2022 10:02:48 +0800 Subject: [PATCH 098/125] github: add release changelog --- .github/workflows/macos-6.2.yml | 17 +++++++++++++++-- .github/workflows/macos.yml | 15 +++++++++++++-- .github/workflows/ubuntu-6.2.yml | 20 ++++++++++++++++---- .github/workflows/ubuntu.yml | 20 ++++++++++++++++---- .github/workflows/windows-6.2.yml | 24 +++++++++++++++++++++--- .github/workflows/windows.yml | 22 ++++++++++++++++++---- 6 files changed, 99 insertions(+), 19 deletions(-) diff --git a/.github/workflows/macos-6.2.yml b/.github/workflows/macos-6.2.yml index b0fe6e9a..501ff491 100644 --- a/.github/workflows/macos-6.2.yml +++ b/.github/workflows/macos-6.2.yml @@ -74,6 +74,13 @@ jobs: mv ${targetName}.app ./tmp # --background "installer_background.png" create-dmg --volname "${targetName} Installer" --volicon "icons/macicon.icns" --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "${targetName}.app" 200 190 --hide-extension "${targetName}.app" --app-drop-link 600 185 --skip-jenkins "${targetName}.dmg" tmp/ + - name: Generate changelog + if: ${{!env.prerelease}} + id: changelog1 + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} + - name: Set outputs id: vars run: | @@ -82,6 +89,11 @@ jobs: echo "::set-output name=release_time::$(date +'%H%M%S')" echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" + + - name: changelog + if: $${{env.prerelease}} + id: changelog2 + run: | previousTag=$(git tag --sort=-creatordate | sed -n 2p) echo "previousTag : $previousTag" @@ -91,7 +103,7 @@ jobs: CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" CHANGELOG="${CHANGELOG//'\"'/'%22'}" CHANGELOG="${CHANGELOG//"'"/ }" - echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" + echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" # tag 上传Release - name: uploadRelease uses: svenstaro/upload-release-action@v2 @@ -119,4 +131,5 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} + ${{ steps.changelog1.outputs.changelog }} + ${{ steps.changelog2.outputs.COMMIT_SUMMARY }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7624d090..6da78257 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -75,7 +75,12 @@ jobs: mv ${targetName}.app ./tmp # --background "installer_background.png" create-dmg --volname "${targetName} Installer" --volicon "icons/macicon.icns" --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "${targetName}.app" 200 190 --hide-extension "${targetName}.app" --app-drop-link 600 185 --skip-jenkins "${targetName}.dmg" tmp/ - + - name: Generate changelog + if: ${{!env.prerelease}} + id: changelog1 + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} - name: Set outputs id: vars @@ -85,6 +90,11 @@ jobs: echo "::set-output name=release_time::$(date +'%H%M%S')" echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" + + - name: changelog + if: $${{env.prerelease}} + id: changelog2 + run: | previousTag=$(git tag --sort=-creatordate | sed -n 2p) echo "previousTag : $previousTag" @@ -122,4 +132,5 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} + ${{ steps.changelog1.outputs.changelog }} + ${{ steps.changelog2.outputs.COMMIT_SUMMARY }} diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index b0d38ee3..812c067b 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -80,15 +80,26 @@ jobs: # with: # name: AppImage # path: './*.AppImage*' + - name: Generate changelog + if: ${{!env.prerelease}} + id: changelog1 + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} + - name: Set outputs id: vars run: | echo "::set-output name=sha_short::$(git rev-parse --short=8 HEAD)" echo "::set-output name=release_date::$(date +'%Y%m%d')" echo "::set-output name=release_time::$(date +'%H%M%S')" - echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" - echo "::set-output name=appname::$(ls *.AppImage*)" + echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" + + - name: changelog + if: $${{env.prerelease}} + id: changelog2 + run: | previousTag=$(git tag --sort=-creatordate | sed -n 2p) echo "previousTag : $previousTag" @@ -98,7 +109,7 @@ jobs: CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" CHANGELOG="${CHANGELOG//'\"'/'%22'}" CHANGELOG="${CHANGELOG//"'"/ }" - echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" + echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" - name: uploadRelease # if: startsWith(github.event.ref, 'refs/tags/') uses: svenstaro/upload-release-action@v2 @@ -126,4 +137,5 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} + ${{ steps.changelog1.outputs.changelog }} + ${{ steps.changelog2.outputs.COMMIT_SUMMARY }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 09022a26..aaf44c8b 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -77,15 +77,26 @@ jobs: chmod a+x linuxdeploy-x86_64.AppImage ./linuxdeploy-x86_64.AppImage --appdir appdir --output appimage --plugin qt -i redist/icons/goldendict.png -d redist/org.goldendict.GoldenDict.desktop + - name: Generate changelog + if: ${{!env.prerelease}} + id: changelog1 + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} + - name: Set outputs id: vars run: | echo "::set-output name=sha_short::$(git rev-parse --short=8 HEAD)" echo "::set-output name=release_date::$(date +'%Y%m%d')" echo "::set-output name=release_time::$(date +'%H%M%S')" - echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" - echo "::set-output name=appname::$(ls *.AppImage*)" + echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" + + - name: changelog + if: $${{env.prerelease}} + id: changelog2 + run: | previousTag=$(git tag --sort=-creatordate | sed -n 2p) echo "previousTag : $previousTag" @@ -95,7 +106,7 @@ jobs: CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" CHANGELOG="${CHANGELOG//'\"'/'%22'}" CHANGELOG="${CHANGELOG//"'"/ }" - echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" + echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" - name: uploadRelease # if: startsWith(github.event.ref, 'refs/tags/') uses: svenstaro/upload-release-action@v2 @@ -123,4 +134,5 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} + ${{ steps.changelog1.outputs.changelog }} + ${{ steps.changelog2.outputs.COMMIT_SUMMARY }} diff --git a/.github/workflows/windows-6.2.yml b/.github/workflows/windows-6.2.yml index 8a227e06..020ba6de 100644 --- a/.github/workflows/windows-6.2.yml +++ b/.github/workflows/windows-6.2.yml @@ -52,15 +52,28 @@ jobs: with: fetch-depth: 0 + - name: Generate changelog + if: ${{!env.prerelease}} + id: changelog1 + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} + - name: Set outputs id: vars shell: bash run: | echo "::set-output name=sha_short::$(git rev-parse --short=8 HEAD)" echo "::set-output name=release_date::$(date +'%Y%m%d')" - echo "::set-output name=release_time::$(date +'%H%M%S')" - echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" + echo "::set-output name=release_time::$(date +'%H%M%S')" + echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" + + - name: changelog + if: $${{env.prerelease}} + id: changelog2 + shell: bash + run: | previousTag=$(git tag --sort=-creatordate | sed -n 2p) echo "previousTag : $previousTag" @@ -70,7 +83,7 @@ jobs: CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" CHANGELOG="${CHANGELOG//'\"'/'%22'}" CHANGELOG="${CHANGELOG//"'"/ }" - echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" + echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" # # msvc编译 - uses: ilammy/msvc-dev-cmd@v1 @@ -128,6 +141,11 @@ jobs: Qt6.X(Universal Build) Qt5.15.2(Intel Kind) auto built by github action. use on your on risk:-) + + CHANGES: + ${{ steps.changelog1.outputs.changelog }} + ${{ steps.changelog2.outputs.COMMIT_SUMMARY }} + - name: upload goldendict.exe only # if: startsWith(github.event.ref, 'refs/tags/') uses: svenstaro/upload-release-action@v2 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c57f1628..2e6cfb78 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,15 +54,28 @@ jobs: with: fetch-depth: 0 + - name: Generate changelog + if: ${{!env.prerelease}} + id: changelog1 + uses: metcalfc/changelog-generator@v3.0.0 + with: + myToken: ${{ secrets.GITHUB_TOKEN }} + - name: Set outputs id: vars shell: bash run: | echo "::set-output name=sha_short::$(git rev-parse --short=8 HEAD)" echo "::set-output name=release_date::$(date +'%Y%m%d')" - echo "::set-output name=release_time::$(date +'%H%M%S')" - echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" + echo "::set-output name=release_time::$(date +'%H%M%S')" + echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" + + - name: changelog + if: $${{env.prerelease}} + id: changelog2 + shell: bash + run: | previousTag=$(git tag --sort=-creatordate | sed -n 2p) echo "previousTag : $previousTag" @@ -72,7 +85,7 @@ jobs: CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" CHANGELOG="${CHANGELOG//'\"'/'%22'}" CHANGELOG="${CHANGELOG//"'"/ }" - echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" + echo "::set-output name=COMMIT_SUMMARY::$(echo "$CHANGELOG")" - uses: ilammy/msvc-dev-cmd@v1 # msvc编译 - name: msvc-build goldendict @@ -127,7 +140,8 @@ jobs: auto built by github action. use on your on risk:-) CHANGES: - ${{ steps.vars.outputs.COMMIT_SUMMARY }} + ${{ steps.changelog1.outputs.changelog }} + ${{ steps.changelog2.outputs.COMMIT_SUMMARY }} - name: upload goldendict.exe only # if: startsWith(github.event.ref, 'refs/tags/') uses: svenstaro/upload-release-action@v2 From 0088377266d256428604471953f053b0d511a7ba Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 14 Jun 2022 07:22:16 +0800 Subject: [PATCH 099/125] clean code:remove useless code --- dsl_details.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/dsl_details.hh b/dsl_details.hh index 1044a919..34eea56d 100644 --- a/dsl_details.hh +++ b/dsl_details.hh @@ -113,7 +113,6 @@ class DslScanner wstring langFrom, langTo; wstring soundDictionary; char readBuffer[ 65536 ]; - QTextStream* fragStream; char * readBufferPtr; LineFeed lineFeed; size_t readBufferLeft; From 53bd9f96dcc23cb10117a1e60adff59f0ac8bc9d Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 14 Jun 2022 20:51:03 +0800 Subject: [PATCH 100/125] concurrent minor changes 1, limit QtConcurrent thread count 2, add mutex to sets operation --- ftshelpers.cc | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 997c05c0..014ee465 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -447,7 +447,19 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QStringList const & words, QRegExp const & searchRegexp ) { - QtConcurrent::blockingMap( offsets, [ & ]( uint32_t offset ) { checkSingleArticle( offset, words, searchRegexp ); } ); + const int parallel_count = QThread::idealThreadCount()/2; + QSemaphore sem( parallel_count < 1 ? 1 : parallel_count ); + for( auto & address : offsets ) + { + if( Utils::AtomicInt::loadAcquire( isCancelled ) ) + return; + sem.acquire(); + QtConcurrent::run( [ & ]() { checkSingleArticle( address, words, searchRegexp ); + sem.release(); + } ); + // QtConcurrent::blockingMap( offsets, + // [ & ]( uint32_t offset ) { checkSingleArticle( offset, words, searchRegexp ); } ); + } } void FTSResultsRequest::checkSingleArticle( uint32_t offset, @@ -681,8 +693,10 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, } links.clear(); - - addressLists<< tmp; + { + Mutex::Lock _( dataMutex ); + addressLists << tmp; + } }; // int n = indexWords.length(); QtConcurrent::blockingMap( indexWords, findLinks ); @@ -785,7 +799,11 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde } links.clear(); - sets<< tmp; + + { + Mutex::Lock _( dataMutex ); + sets << tmp; + } }; QtConcurrent::blockingMap( hieroglyphsList, fn_wordLink ); From 206474364aa1bec99d4699b24bfb8ef8f7febd3b Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Mon, 13 Jun 2022 22:12:03 +0800 Subject: [PATCH 101/125] feat:mdx lock free when loadArticle --- chunkedstorage.cc | 30 ++++++++++++++++++++---------- file.cc | 14 ++++++++++++++ file.hh | 2 ++ ftshelpers.cc | 8 ++++---- mdx.cc | 6 +++--- 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/chunkedstorage.cc b/chunkedstorage.cc index aaf11e88..ff129f81 100644 --- a/chunkedstorage.cc +++ b/chunkedstorage.cc @@ -4,6 +4,7 @@ #include "chunkedstorage.hh" #include #include +#include namespace ChunkedStorage { @@ -134,25 +135,34 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) // Read and decompress the chunk { - file.seek( offsets[ chunkIdx ] ); + // file.seek( offsets[ chunkIdx ] ); - uint32_t uncompressedSize = file.read< uint32_t >(); - uint32_t compressedSize = file.read< uint32_t >(); + auto bytes = file.map( offsets[ chunkIdx ], 8 ); + auto qBytes = QByteArray::fromRawData( reinterpret_cast< char * >(bytes), 8 ); + QDataStream in( qBytes ); + in.setByteOrder( QDataStream::LittleEndian ); + uint32_t uncompressedSize; + uint32_t compressedSize; + // = file.read< uint32_t >(); + + in >> uncompressedSize >> compressedSize; + + file.unmap( bytes ); chunk.resize( uncompressedSize ); - vector< unsigned char > compressedData( compressedSize ); + // vector< unsigned char > compressedData( compressedSize ); + auto chunkDataBytes = file.map( offsets[ chunkIdx ] + 8, compressedSize ); - file.read( &compressedData.front(), compressedData.size() ); + // file.read( &compressedData.front(), compressedData.size() ); unsigned long decompressedLength = chunk.size(); - if ( uncompress( (unsigned char *)&chunk.front(), - &decompressedLength, - &compressedData.front(), - compressedData.size() ) != Z_OK || - decompressedLength != chunk.size() ) + if( uncompress( (unsigned char *)&chunk.front(), &decompressedLength, chunkDataBytes, compressedSize ) != Z_OK + || decompressedLength != chunk.size() ) throw exFailedToDecompressChunk(); + + file.unmap( chunkDataBytes ); } size_t offsetInChunk = address & 0xffFF; diff --git a/file.cc b/file.cc index 6050eef3..dabc8853 100644 --- a/file.cc +++ b/file.cc @@ -256,6 +256,20 @@ void Class::seek( qint64 offset ) throw exSeekError(); } +uchar * Class::map( qint64 offset, qint64 size ) +{ + if( writeBuffer ) + flushWriteBuffer(); + + return f.map( offset, size ); +} + +bool Class::unmap( uchar * address ) +{ + return f.unmap( address ); +} + + void Class::seekCur( qint64 offset ) { if ( writeBuffer ) diff --git a/file.hh b/file.hh index c4c1c50d..060c6b13 100644 --- a/file.hh +++ b/file.hh @@ -95,6 +95,7 @@ public: /// Seeks in the file, relative to its beginning. void seek( qint64 offset ) ; + uchar * map( qint64 offset, qint64 size ); /// Seeks in the file, relative to the current position. void seekCur( qint64 offset ) ; /// Seeks in the file, relative to the end of file. @@ -117,6 +118,7 @@ public: void close() ; ~Class() noexcept; + bool unmap( uchar * address ); private: diff --git a/ftshelpers.cc b/ftshelpers.cc index 014ee465..6f86673d 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -679,7 +679,7 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, vector< char > chunk; char * linksPtr; { - Mutex::Lock _( dict.getFtsMutex() ); + // Mutex::Lock _( dict.getFtsMutex() ); linksPtr = chunks->getBlock( links[ x ].articleOffset, chunk ); } @@ -785,7 +785,7 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde vector< char > chunk; char * linksPtr; { - Mutex::Lock _( dict.getFtsMutex() ); + // Mutex::Lock _( dict.getFtsMutex() ); linksPtr = chunks->getBlock( links[ x ].articleOffset, chunk ); } @@ -844,7 +844,7 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde vector< char > chunk; char * linksPtr; { - Mutex::Lock _( dict.getFtsMutex() ); + // Mutex::Lock _( dict.getFtsMutex() ); linksPtr = chunks->getBlock( links[ x ].articleOffset, chunk ); } @@ -935,7 +935,7 @@ void FTSResultsRequest::fullIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, vector< char > chunk; char * linksPtr; { - Mutex::Lock _( dict.getFtsMutex() ); + // Mutex::Lock _( dict.getFtsMutex() ); linksPtr = chunks->getBlock( links[ x ].articleOffset, chunk ); } diff --git a/mdx.cc b/mdx.cc index 8998bfa5..4eaeb40f 100644 --- a/mdx.cc +++ b/mdx.cc @@ -163,7 +163,7 @@ public: MdictParser::RecordInfo indexEntry; vector< char > chunk; - Mutex::Lock _( idxMutex ); + // Mutex::Lock _( idxMutex ); const char * indexEntryPtr = chunks.getBlock( links[ 0 ].articleOffset, chunk ); memcpy( &indexEntry, indexEntryPtr, sizeof( indexEntry ) ); @@ -867,7 +867,7 @@ const QString & MdxDictionary::getDescription() } else { - Mutex::Lock _( idxMutex ); + // Mutex::Lock _( idxMutex ); vector< char > chunk; char * dictDescription = chunks.getBlock( idxHeader.descriptionAddress, chunk ); string str( dictDescription ); @@ -900,7 +900,7 @@ void MdxDictionary::loadIcon() noexcept void MdxDictionary::loadArticle( uint32_t offset, string & articleText, bool noFilter ) { vector< char > chunk; - Mutex::Lock _( idxMutex ); + // Mutex::Lock _( idxMutex ); // Load record info from index MdictParser::RecordInfo recordInfo; From 4cf184d646ed3dbe7357db45942e4cac6c00e564 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 15 Jun 2022 07:23:02 +0800 Subject: [PATCH 102/125] fix upstream merge conflict --- articleview.cc | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/articleview.cc b/articleview.cc index ad90cbea..bee9e849 100644 --- a/articleview.cc +++ b/articleview.cc @@ -535,13 +535,6 @@ void ArticleView::loadFinished( bool result ) // Expand collapsed article if only one loaded ui.definition->page()->runJavaScript( QString( "gdCheckArticlesNumber();" ) ); - // Jump to current article after page reloading - if( !articleToJump.isEmpty() ) - { - setCurrentArticle( articleToJump, true ); - articleToJump.clear(); - } - if( !Utils::Url::queryItemValue( url, "gdanchor" ).isEmpty() ) { QString anchor = QUrl::fromPercentEncoding( Utils::Url::encodedQueryItemValue( url, "gdanchor" ) ); @@ -1623,20 +1616,6 @@ void ArticleView::forward() void ArticleView::reload() { - QMap< QString, QVariant > userData = ui.definition->history()->currentItem().userData().toMap(); - - // Save current article, which can be empty - userData[ "currentArticle" ] = getCurrentArticle(); - - // Remove saved window position. Reloading occurs in response to changes that - // may affect content height, so restoring the current window position can cause - // uncontrolled jumps. Scrolling to the current article (i.e. jumping to the top - // of it) is simple, reliable and predictable, if not ideal. - userData[ "sx" ].clear(); - userData[ "sy" ].clear(); - - ui.definition->history()->currentItem().setUserData( userData ); - ui.definition->reload(); } From 5571f877bcfdb577a0b4e788d5a573121d8c9efb Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Wed, 15 Jun 2022 08:12:27 +0800 Subject: [PATCH 103/125] fix: F12 inspect windows open on the first time --- article_inspect.cpp | 7 ++++--- articleview.cc | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/article_inspect.cpp b/article_inspect.cpp index 71a0f8f3..aa20032a 100644 --- a/article_inspect.cpp +++ b/article_inspect.cpp @@ -26,13 +26,14 @@ void ArticleInspector::setInspectPage( QWebEngineView * view ) { page->triggerAction( QWebEnginePage::InspectElement ); } -#else - page->triggerAction( QWebEnginePage::InspectElement ); -#endif if( !firstTimeOpened ) { firstTimeOpened = true; } +#else + page->triggerAction( QWebEnginePage::InspectElement ); +#endif + raise(); show(); } diff --git a/articleview.cc b/articleview.cc index bee9e849..6effd956 100644 --- a/articleview.cc +++ b/articleview.cc @@ -1621,7 +1621,7 @@ void ArticleView::reload() void ArticleView::hasSound( const std::function< void( bool ) > & callback ) { - ui.definition->page()->runJavaScript( "gdAudioLinks.first", + ui.definition->page()->runJavaScript( "if(typeof(gdAudioLinks)!=\"undefined\") gdAudioLinks.first", [ callback ]( const QVariant & v ) { bool has = false; From f52251d640fb42b4892aced8519d14a150ca88cd Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 08:00:24 +0800 Subject: [PATCH 104/125] fix:mdict compact html parse style error --- mdictparser.cc | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/mdictparser.cc b/mdictparser.cc index bc8eaf90..931a8fff 100644 --- a/mdictparser.cc +++ b/mdictparser.cc @@ -336,6 +336,23 @@ bool MdictParser::readHeader( QDataStream & in ) headerTextUtf16.clear(); in.setByteOrder( QDataStream::BigEndian ); + + //parse stylesheet + QString styleSheets; + + if( headerText.contains( "StyleSheet" ) ) + { + // a workaround to bypass https://bugreports.qt.io/browse/QTBUG-102612 + QRegularExpression rx( "StyleSheet=\"([^\"]*?)\"", QRegularExpression::CaseInsensitiveOption ); + + auto match = rx.match( headerText ); + + if( match.hasMatch() || match.hasPartialMatch() ) + { + styleSheets = match.captured( 1 ); + } + } + //with this control character ,qt6.x can not parse attribute value. headerText.remove(QRegularExpression("\\p{C}")); @@ -355,32 +372,14 @@ bool MdictParser::readHeader( QDataStream & in ) // styleId # 1-255 // style.prefix // style.suffix - if ( headerAttributes.contains( "StyleSheet" ) ) + if ( !styleSheets.isEmpty() ) { -#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) - //a workaround to bypass https://bugreports.qt.io/browse/QTBUG-102612 - QRegularExpression rx( "StyleSheet=\"([^\"]*?)\"", QRegularExpression::CaseInsensitiveOption ); - - auto match = rx.match( headerText ); - QString styleSheets; - - if( match.hasMatch() || match.hasPartialMatch() ) - { - styleSheets = match.captured( 1 ); - } -#else - QString styleSheets = headerAttributes.namedItem( "StyleSheet" ).toAttr().value(); -#endif QStringList lines = styleSheets.split( QRegularExpression( "[\r\n]" ), Qt::KeepEmptyParts ); for( int i = 0; i < lines.size() - 3; i += 3 ) { -#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) styleSheets_[ lines[ i ].toInt() ] = - pair< QString, QString >( Html::fromHtmlEscaped( lines[ i + 1 ] ), Html::fromHtmlEscaped( lines[ i + 2 ] ) ); -#else - styleSheets_[ lines[ i ].toInt() ] = pair< QString, QString >( lines[ i + 1 ], lines[ i + 2 ] ); -#endif + pair( Html::fromHtmlEscaped( lines[ i + 1 ] ), Html::fromHtmlEscaped( lines[ i + 2 ] ) ); } } From 6f9c1067a3d33132af2dd8d9f28e4c593c4e0ce7 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Tue, 14 Jun 2022 21:47:51 +0800 Subject: [PATCH 105/125] opt: optimize cjk fulltext search 1, add concurrent parallel task limitaion --- ftshelpers.cc | 80 ++++++++++++++------------------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 6f86673d..724f2498 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -454,9 +454,11 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, if( Utils::AtomicInt::loadAcquire( isCancelled ) ) return; sem.acquire(); - QtConcurrent::run( [ & ]() { checkSingleArticle( address, words, searchRegexp ); + QFuture f =QtConcurrent::run( [ & ]() { checkSingleArticle( address, words, searchRegexp ); sem.release(); } ); + f.waitForFinished(); + // QtConcurrent::blockingMap( offsets, // [ & ]( uint32_t offset ) { checkSingleArticle( offset, words, searchRegexp ); } ); } @@ -466,7 +468,6 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, QStringList const & words, QRegExp const & searchRegexp ) { - qDebug()<<"checking"< offsetsForHeadwords; @@ -508,7 +509,10 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, if( headword.isEmpty() ) offsetsForHeadwords.append( offset ); else + { + Mutex::Lock _( dataMutex ); foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + } ++results; if( maxResults > 0 && results >= maxResults ) @@ -610,7 +614,10 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, if( headword.isEmpty() ) offsetsForHeadwords.append( offset ); else + { + Mutex::Lock _( dataMutex ); foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + } ++results; if( maxResults > 0 && results >= maxResults ) @@ -625,7 +632,10 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, if( headword.isEmpty() ) offsetsForHeadwords.append( offset ); else + { + Mutex::Lock _( dataMutex ); foundHeadwords->append( FTS::FtsHeadword( headword, id, QStringList(), matchCase ) ); + } ++results; if( maxResults > 0 && results >= maxResults ) @@ -639,10 +649,13 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, QVector< QString > headwords; dict.getHeadwordsFromOffsets( offsetsForHeadwords, headwords, &isCancelled ); for( int x = 0; x < headwords.size(); x++ ) + { + Mutex::Lock _( dataMutex ); foundHeadwords->append( FTS::FtsHeadword( headwords.at( x ), id, x < hiliteRegExps.size() ? hiliteRegExps.at( x ) : QStringList(), matchCase ) ); + } } } @@ -765,12 +778,15 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde int n = wordsList.size(); if( !hieroglyphsList.isEmpty() ) + { + wordsList += hieroglyphsList; n += 1; + } allWordsLinks.resize( n ); int wordNom = 0; - if( !hieroglyphsList.empty() ) + if( !wordsList.empty() ) { QList< QSet< uint32_t > > sets; auto fn_wordLink = [ & ](const QString & word ) @@ -805,7 +821,7 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde sets << tmp; } }; - QtConcurrent::blockingMap( hieroglyphsList, fn_wordLink ); + QtConcurrent::blockingMap( wordsList, fn_wordLink ); int i = 0; for( auto & elem : sets ) @@ -816,63 +832,11 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde setOfOffsets = setOfOffsets.intersect( elem ); } - allWordsLinks[ wordNom ] = setOfOffsets; - setOfOffsets.clear(); + // allWordsLinks[ wordNom ] = setOfOffsets; + // setOfOffsets.clear(); wordNom += 1; } - if( !wordsList.isEmpty() ) - { - QVector< BtreeIndexing::WordArticleLink > links; - links.reserve( wordsInIndex ); - ftsIndex.findArticleLinks( &links, 0, 0, &isCancelled ); - - for( int x = 0; x < links.size(); x++ ) - { - if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - return; - - QString word = QString::fromUtf8( links[ x ].word.data(), links[ x ].word.size() ); - - if( ignoreDiacritics ) - word = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( word ) ) ); - - for( int i = 0; i < wordsList.size(); i++ ) - { - if( word.length() >= wordsList.at( i ).length() && word.contains( wordsList.at( i ) ) ) - { - vector< char > chunk; - char * linksPtr; - { - // Mutex::Lock _( dict.getFtsMutex() ); - linksPtr = chunks->getBlock( links[ x ].articleOffset, chunk ); - } - - memcpy( &size, linksPtr, sizeof(uint32_t) ); - linksPtr += sizeof(uint32_t); - for( uint32_t y = 0; y < size; y++ ) - { - allWordsLinks[ wordNom ].insert( *( reinterpret_cast< uint32_t * >( linksPtr ) ) ); - linksPtr += sizeof(uint32_t); - } - wordNom += 1; - if( searchMode == FTS::PlainText || searchMode == FTS::WholeWords ) - break; - } - } - } - - links.clear(); - } - - for( int i = 0; i < allWordsLinks.size(); i++ ) - { - if( i == 0 ) - setOfOffsets = allWordsLinks.at( i ); - else - setOfOffsets = setOfOffsets.intersect( allWordsLinks.at( i ) ); - } - if( setOfOffsets.isEmpty() ) return; From 4b9d71376df19d0e4b65f33d6b43d348345ecbcc Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 07:53:14 +0800 Subject: [PATCH 106/125] remove javascript highlight logic webengine's findtext(highlight) has little features compared to webkits --- articleview.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/articleview.cc b/articleview.cc index 6effd956..7fe9d9f4 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2558,10 +2558,10 @@ void ArticleView::highlightFTSResults() // highlightAllFtsOccurences( flags ); ui.definition->findText( allMatches.at( 0 ), flags ); // if( ui.definition->findText( allMatches.at( 0 ), flags ) ) - { - ui.definition->page()->runJavaScript( - QString( "%1=window.getSelection().getRangeAt(0);_=0;" ).arg( rangeVarName ) ); - } + // { + // ui.definition->page()->runJavaScript( + // QString( "%1=window.getSelection().getRangeAt(0);_=0;" ).arg( rangeVarName ) ); + // } } ui.ftsSearchFrame->show(); @@ -2673,9 +2673,9 @@ void ArticleView::performFtsFindOperation( bool backwards ) } #endif // Store new highlighted selection - ui.definition->page()-> - runJavaScript( QString( "%1=window.getSelection().getRangeAt(0);_=0;" ) - .arg( rangeVarName ) ); + // ui.definition->page()-> + // runJavaScript( QString( "%1=window.getSelection().getRangeAt(0);_=0;" ) + // .arg( rangeVarName ) ); } void ArticleView::on_ftsSearchPrevious_clicked() From 79e2f7c0987f7ab8045a318d5a16ae5a42b135c6 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 08:18:59 +0800 Subject: [PATCH 107/125] unmap file when throw exception --- chunkedstorage.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chunkedstorage.cc b/chunkedstorage.cc index ff129f81..3133b651 100644 --- a/chunkedstorage.cc +++ b/chunkedstorage.cc @@ -160,7 +160,10 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) if( uncompress( (unsigned char *)&chunk.front(), &decompressedLength, chunkDataBytes, compressedSize ) != Z_OK || decompressedLength != chunk.size() ) + { + file.unmap( chunkDataBytes ); throw exFailedToDecompressChunk(); + } file.unmap( chunkDataBytes ); } From f2ba7fc6c00165d2ef1a6572a463e220d104281b Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Thu, 16 Jun 2022 10:06:19 +0800 Subject: [PATCH 108/125] Update ubuntu.yml --- .github/workflows/ubuntu.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index aaf44c8b..d887d374 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -92,7 +92,8 @@ jobs: echo "::set-output name=release_time::$(date +'%H%M%S')" echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" - + echo "::set-output name=appname::$(ls *.AppImage*)" + - name: changelog if: $${{env.prerelease}} id: changelog2 From c42326834a771a459f0b12036c4ceb794f63bbde Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Thu, 16 Jun 2022 10:06:46 +0800 Subject: [PATCH 109/125] Update ubuntu-6.2.yml --- .github/workflows/ubuntu-6.2.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index 812c067b..df09c035 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -95,7 +95,8 @@ jobs: echo "::set-output name=release_time::$(date +'%H%M%S')" echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')" echo "::set-output name=release_hm::$(date +'%y%m%d')" - + echo "::set-output name=appname::$(ls *.AppImage*)" + - name: changelog if: $${{env.prerelease}} id: changelog2 From 02f7fc004819b0ff1c79017e3ab8baace69a3903 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 20:17:07 +0800 Subject: [PATCH 110/125] opt: concurrent limitation and auto release semaphore --- ftshelpers.cc | 44 +++++++++++++++++++++++++++----------------- ftshelpers.hh | 5 ++++- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 724f2498..e4a6325c 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -449,34 +449,29 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, { const int parallel_count = QThread::idealThreadCount()/2; QSemaphore sem( parallel_count < 1 ? 1 : parallel_count ); + + QFutureSynchronizer< void > synchronizer; + auto searchRegularExpression = createMatchRegex( searchRegexp ); + for( auto & address : offsets ) { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) return; sem.acquire(); - QFuture f =QtConcurrent::run( [ & ]() { checkSingleArticle( address, words, searchRegexp ); - sem.release(); + QFuture< void > f = QtConcurrent::run( + [ & ]() + { + QSemaphoreReleaser releaser( sem ); + checkSingleArticle( address, words, searchRegularExpression ); } ); - f.waitForFinished(); - - // QtConcurrent::blockingMap( offsets, - // [ & ]( uint32_t offset ) { checkSingleArticle( offset, words, searchRegexp ); } ); + synchronizer.addFuture( f ); } } -void FTSResultsRequest::checkSingleArticle( uint32_t offset, - QStringList const & words, - QRegExp const & searchRegexp ) +QRegularExpression FTSResultsRequest::createMatchRegex( QRegExp const & searchRegexp ) { - // int results = 0; - QString headword, articleText; - QList< uint32_t > offsetsForHeadwords; - QVector< QStringList > hiliteRegExps; - - QString id = QString::fromUtf8( dict.getId().c_str() ); - - // RegExp mode QRegularExpression searchRegularExpression; + if( searchMode == FTS::Wildcards ) searchRegularExpression.setPattern( wildcardsToRegexp( searchRegexp.pattern() ) ); else @@ -489,6 +484,21 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, searchRegularExpression.setPatternOptions( patternOptions ); if( !searchRegularExpression.isValid() ) searchRegularExpression.setPattern( "" ); + return searchRegularExpression; +} + +void FTSResultsRequest::checkSingleArticle( uint32_t offset, + QStringList const & words, + QRegularExpression const & searchRegularExpression ) +{ + // int results = 0; + QString headword, articleText; + QList< uint32_t > offsetsForHeadwords; + QVector< QStringList > hiliteRegExps; + + QString id = QString::fromUtf8( dict.getId().c_str() ); + + // RegExp mode if( searchMode == FTS::Wildcards || searchMode == FTS::RegExp ) { diff --git a/ftshelpers.hh b/ftshelpers.hh index 4bd309ac..5a5ac6ea 100644 --- a/ftshelpers.hh +++ b/ftshelpers.hh @@ -89,8 +89,11 @@ class FTSResultsRequest : public Dictionary::DataRequest void checkArticles( QVector< uint32_t > const & offsets, QStringList const & words, QRegExp const & searchRegexp = QRegExp() ); + QRegularExpression createMatchRegex( QRegExp const & searchRegexp ); - void checkSingleArticle( uint32_t offset, QStringList const & words, QRegExp const & searchRegexp = QRegExp() ); + void checkSingleArticle( uint32_t offset, + QStringList const & words, + QRegularExpression const & searchRegexp = QRegularExpression() ); void indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, sptr< ChunkedStorage::Reader > chunks, From 947323854aef00dc1251f34bbf0ca9462b7dc7a6 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 20:20:33 +0800 Subject: [PATCH 111/125] remove extra parantheses --- ftshelpers.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index e4a6325c..497fede3 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -503,7 +503,6 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, if( searchMode == FTS::Wildcards || searchMode == FTS::RegExp ) { // for( int i = 0; i < offsets.size(); i++ ) - { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) return; @@ -528,7 +527,6 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, if( maxResults > 0 && results >= maxResults ) return; } - } } else { @@ -542,7 +540,6 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, } // for( int i = 0; i < offsets.size(); i++ ) - { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) return; @@ -652,15 +649,15 @@ void FTSResultsRequest::checkSingleArticle( uint32_t offset, return; } } - } } if( !offsetsForHeadwords.isEmpty() ) { QVector< QString > headwords; + Mutex::Lock _( dataMutex ); + dict.getHeadwordsFromOffsets( offsetsForHeadwords, headwords, &isCancelled ); for( int x = 0; x < headwords.size(); x++ ) { - Mutex::Lock _( dataMutex ); foundHeadwords->append( FTS::FtsHeadword( headwords.at( x ), id, x < hiliteRegExps.size() ? hiliteRegExps.at( x ) : QStringList(), @@ -844,7 +841,7 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde // allWordsLinks[ wordNom ] = setOfOffsets; // setOfOffsets.clear(); - wordNom += 1; + // wordNom += 1; } if( setOfOffsets.isEmpty() ) From 2da252539ac38df613cd0dadcfe5b81d6eb75b56 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 20:34:32 +0800 Subject: [PATCH 112/125] auto file unmap --- chunkedstorage.cc | 9 ++++++--- ftshelpers.cc | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/chunkedstorage.cc b/chunkedstorage.cc index 3133b651..33de4099 100644 --- a/chunkedstorage.cc +++ b/chunkedstorage.cc @@ -5,6 +5,7 @@ #include #include #include +#include namespace ChunkedStorage { @@ -144,7 +145,6 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) uint32_t uncompressedSize; uint32_t compressedSize; - // = file.read< uint32_t >(); in >> uncompressedSize >> compressedSize; @@ -155,17 +155,20 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) auto chunkDataBytes = file.map( offsets[ chunkIdx ] + 8, compressedSize ); // file.read( &compressedData.front(), compressedData.size() ); + auto autoUnmap = qScopeGuard( + [ & ] { + file.unmap( chunkDataBytes ); + } ); + Q_UNUSED( autoUnmap ) unsigned long decompressedLength = chunk.size(); if( uncompress( (unsigned char *)&chunk.front(), &decompressedLength, chunkDataBytes, compressedSize ) != Z_OK || decompressedLength != chunk.size() ) { - file.unmap( chunkDataBytes ); throw exFailedToDecompressChunk(); } - file.unmap( chunkDataBytes ); } size_t offsetInChunk = address & 0xffFF; diff --git a/ftshelpers.cc b/ftshelpers.cc index 497fede3..85f0abf4 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -19,6 +19,8 @@ #include "wildcard.hh" #include #include "base/globalregex.hh" +#include +#include using std::vector; using std::string; From 1830415da59a5984b6b730f938fcf30575afa857 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 21:03:58 +0800 Subject: [PATCH 113/125] fix: highlight a rare case when toPlainText omit space between two words --- articleview.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/articleview.cc b/articleview.cc index 7fe9d9f4..82bcc868 100644 --- a/articleview.cc +++ b/articleview.cc @@ -2503,6 +2503,12 @@ void ArticleView::highlightFTSResults() else regString = regString.remove( AccentMarkHandler::accentMark() ); + //
watchout
to plainText will return "watchout". + //if application goes here,that means the article text must contains the search text. + //whole word match regString will contain \b . can not match the above senario. + //workaround ,remove \b from the regstring="(\bwatch\b)" + regString.remove( QRegularExpression( "\\\\b" ) ); + QRegularExpression regexp; if( Utils::Url::hasQueryItem( url, "wildcards" ) ) regexp.setPattern( wildcardsToRegexp( regString ) ); From 5c4a968781d5d15a4753c9f91c04e03819653f5c Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Thu, 16 Jun 2022 21:08:01 +0800 Subject: [PATCH 114/125] github:upgrade qt6.3.0 to qt6.3.1 --- .github/workflows/macos-6.2.yml | 2 +- .github/workflows/ubuntu-6.2.yml | 2 +- .github/workflows/windows-6.2.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos-6.2.yml b/.github/workflows/macos-6.2.yml index 501ff491..26accb9b 100644 --- a/.github/workflows/macos-6.2.yml +++ b/.github/workflows/macos-6.2.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: os: [macos-11] - qt_ver: [6.2.4,6.3.0] + qt_ver: [6.2.4,6.3.1] qt_arch: [clang_64] env: targetName: GoldenDict diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index df09c035..71e277d3 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - qt_ver: [6.2.4,6.3.0] + qt_ver: [6.2.4,6.3.1] qt_arch: [gcc_64] env: version: 22.5.22 diff --git a/.github/workflows/windows-6.2.yml b/.github/workflows/windows-6.2.yml index 020ba6de..3ee25c98 100644 --- a/.github/workflows/windows-6.2.yml +++ b/.github/workflows/windows-6.2.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [windows-2019] - qt_ver: [6.2.4,6.3.0] + qt_ver: [6.2.4,6.3.1] qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe From ceb31baf9762f106f13f6a1c747b806e4f6df362 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 17 Jun 2022 20:23:16 +0800 Subject: [PATCH 115/125] add mutex to getBlock --- chunkedstorage.cc | 2 +- file.hh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/chunkedstorage.cc b/chunkedstorage.cc index 33de4099..69b155fe 100644 --- a/chunkedstorage.cc +++ b/chunkedstorage.cc @@ -137,7 +137,7 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) // Read and decompress the chunk { // file.seek( offsets[ chunkIdx ] ); - + Mutex::Lock _( file.lock ); auto bytes = file.map( offsets[ chunkIdx ], 8 ); auto qBytes = QByteArray::fromRawData( reinterpret_cast< char * >(bytes), 8 ); QDataStream in( qBytes ); diff --git a/file.hh b/file.hh index 060c6b13..a69f3840 100644 --- a/file.hh +++ b/file.hh @@ -9,6 +9,7 @@ #include #include #include "ex.hh" +#include "mutex.hh" /// A simple wrapper over FILE * operations with added write-buffering, /// used for non-Qt parts of code. @@ -44,6 +45,7 @@ class Class void open( char const * filename, char const * mode ) ; public: + Mutex lock; Class( char const * filename, char const * mode ) ; From fa72de7b170f3c9fd1a848bbbd990040bc175b08 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 17 Jun 2022 20:28:29 +0800 Subject: [PATCH 116/125] github:ubuntu 6.x appimage --- .github/workflows/ubuntu-6.2.yml | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index 71e277d3..b18377d6 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - qt_ver: [6.2.4,6.3.1] + qt_ver: [6.3.1] qt_arch: [gcc_64] env: version: 22.5.22 @@ -65,21 +65,18 @@ jobs: qmake CONFIG+=release PREFIX=/usr CONFIG+=zim_support CONFIG+=chinese_conversion_support make INSTALL_ROOT=appdir -j`nproc` install; find appdir/ + ls -al appdir #copy missing shared dll to appdir. - mkdir -p appdir/usr/lib - cp $(ldd appdir/usr/bin/goldendict | grep -o '\W/[^ ]*' |grep gobject ) appdir/usr/lib - cp $(ldd appdir/usr/bin/goldendict | grep -o '\W/[^ ]*' |grep libpango ) appdir/usr/lib + # mkdir -p appdir/usr/lib + # cp $(ldd appdir/usr/bin/goldendict | grep -o '\W/[^ ]*' |grep gobject ) appdir/usr/lib + # cp $(ldd appdir/usr/bin/goldendict | grep -o '\W/[^ ]*' |grep libpango ) appdir/usr/lib - name: Build AppImage run: | - wget -c -nv "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" - chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage - wget -c -nv "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" - chmod a+x linuxdeploy-x86_64.AppImage - ./linuxdeploy-x86_64.AppImage --appdir appdir --output appimage --plugin qt -i redist/icons/goldendict.png -d redist/org.goldendict.GoldenDict.desktop - # - uses: actions/upload-artifact@v2 - # with: - # name: AppImage - # path: './*.AppImage*' + wget -c https://github.com/$(wget -q https://github.com/probonopd/go-appimage/releases -O - | grep "appimagetool-.*-x86_64.AppImage" | head -n 1 | cut -d '"' -f 2) + chmod +x appimagetool-*.AppImage + ./appimagetool-*.AppImage -s deploy appdir/usr/share/applications/*.desktop # Bundle EVERYTHING + # ./linuxdeploy-x86_64.AppImage --appdir appdir --output appimage --plugin qt -i redist/icons/goldendict.png -d redist/org.goldendict.GoldenDict.desktop + - name: Generate changelog if: ${{!env.prerelease}} id: changelog1 From 2f3af9d7d39047ed4ceb5b1b8653f0668571f5a7 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 17 Jun 2022 20:37:59 +0800 Subject: [PATCH 117/125] map nullptr check --- chunkedstorage.cc | 5 ++++- chunkedstorage.hh | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/chunkedstorage.cc b/chunkedstorage.cc index 69b155fe..26e32cde 100644 --- a/chunkedstorage.cc +++ b/chunkedstorage.cc @@ -139,6 +139,8 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) // file.seek( offsets[ chunkIdx ] ); Mutex::Lock _( file.lock ); auto bytes = file.map( offsets[ chunkIdx ], 8 ); + if( bytes == nullptr ) + throw mapFailed(); auto qBytes = QByteArray::fromRawData( reinterpret_cast< char * >(bytes), 8 ); QDataStream in( qBytes ); in.setByteOrder( QDataStream::LittleEndian ); @@ -153,7 +155,8 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) // vector< unsigned char > compressedData( compressedSize ); auto chunkDataBytes = file.map( offsets[ chunkIdx ] + 8, compressedSize ); - + if( chunkDataBytes == nullptr ) + throw mapFailed(); // file.read( &compressedData.front(), compressedData.size() ); auto autoUnmap = qScopeGuard( [ & ] { diff --git a/chunkedstorage.hh b/chunkedstorage.hh index b92c6aed..4132e30b 100644 --- a/chunkedstorage.hh +++ b/chunkedstorage.hh @@ -25,6 +25,7 @@ DEF_EX( Ex, "Chunked storage exception", std::exception ) DEF_EX( exFailedToCompressChunk, "Failed to compress a chunk", Ex ) DEF_EX( exAddressOutOfRange, "The given chunked address is out of range", Ex ) DEF_EX( exFailedToDecompressChunk, "Failed to decompress a chunk", Ex ) +DEF_EX( mapFailed, "Failed to map/unmap the file", Ex ) /// This class writes data blocks in chunks. class Writer From 6d5ff66008ba0588b6b1412592bd302a2137dfbc Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Fri, 17 Jun 2022 21:41:45 +0800 Subject: [PATCH 118/125] feat: cancel fulltext search --- ftshelpers.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ftshelpers.cc b/ftshelpers.cc index 85f0abf4..160d0140 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -458,7 +458,10 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, for( auto & address : offsets ) { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) + { + synchronizer.setCancelOnWait(true); return; + } sem.acquire(); QFuture< void > f = QtConcurrent::run( [ & ]() From 7a43cc366e219d05c8cefc320505914801939dee Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 09:09:40 +0800 Subject: [PATCH 119/125] use qmutex instead of mutex(qmutexrecursive) --- chunkedstorage.cc | 2 +- file.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chunkedstorage.cc b/chunkedstorage.cc index 26e32cde..54a7c13c 100644 --- a/chunkedstorage.cc +++ b/chunkedstorage.cc @@ -137,7 +137,7 @@ char * Reader::getBlock( uint32_t address, vector< char > & chunk ) // Read and decompress the chunk { // file.seek( offsets[ chunkIdx ] ); - Mutex::Lock _( file.lock ); + QMutexLocker _( &file.lock ); auto bytes = file.map( offsets[ chunkIdx ], 8 ); if( bytes == nullptr ) throw mapFailed(); diff --git a/file.hh b/file.hh index a69f3840..fa819306 100644 --- a/file.hh +++ b/file.hh @@ -45,7 +45,7 @@ class Class void open( char const * filename, char const * mode ) ; public: - Mutex lock; + QMutex lock; Class( char const * filename, char const * mode ) ; From 3d3088738ecc8541212649eb17f872cd6f9e38b4 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 09:17:43 +0800 Subject: [PATCH 120/125] revert mutex parent class from qrecursivemutex to qmutex --- mutex.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mutex.hh b/mutex.hh index 5a6ce6a1..694ae698 100644 --- a/mutex.hh +++ b/mutex.hh @@ -4,16 +4,16 @@ #ifndef __MUTEX_HH_INCLUDED__ #define __MUTEX_HH_INCLUDED__ -#include +#include /// This provides a mutex class. As you can see, it's just a Qt one, but it /// does provide the Lock class which doesn't seem to exist in Qt, and it does /// provide some abstraction for dictionaries in case they are to be ported /// away from Qt. -class Mutex : public QRecursiveMutex +class Mutex : public QMutex { public: - Mutex() : QRecursiveMutex() + Mutex() : QMutex() {} ~Mutex() {} From 9472e1a06c4b88e9aa6a83a5315c7838dfa4c7b9 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 12:50:12 +0800 Subject: [PATCH 121/125] code simplified --- ftshelpers.cc | 58 +++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index 160d0140..a12325be 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -691,15 +691,20 @@ void FTSResultsRequest::indexSearch( BtreeIndexing::BtreeIndex & ftsIndex, uint32_t size; if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - addressLists<< tmp; + { + addressLists << tmp; + return; + } vector< BtreeIndexing::WordArticleLink > links = ftsIndex.findArticles( gd::toWString( word ), ignoreDiacritics ); for( unsigned x = 0; x < links.size(); x++ ) { - if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - addressLists<< tmp; + { + addressLists << tmp; + return; + } vector< char > chunk; char * linksPtr; @@ -796,7 +801,6 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde } allWordsLinks.resize( n ); - int wordNom = 0; if( !wordsList.empty() ) { @@ -808,7 +812,10 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde for( unsigned x = 0; x < links.size(); x++ ) { if( Utils::AtomicInt::loadAcquire( isCancelled ) ) - sets<< tmp; + { + sets << tmp; + return; + } vector< char > chunk; char * linksPtr; @@ -819,6 +826,7 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde memcpy( &size, linksPtr, sizeof(uint32_t) ); linksPtr += sizeof(uint32_t); + tmp.reserve( size ); for( uint32_t y = 0; y < size; y++ ) { tmp.insert( *( reinterpret_cast< uint32_t * >( linksPtr ) ) ); @@ -854,16 +862,16 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde allWordsLinks.clear(); - QVector< uint32_t > offsets; - offsets.resize( setOfOffsets.size() ); - uint32_t * ptr = &offsets.front(); - - for( QSet< uint32_t >::ConstIterator it = setOfOffsets.constBegin(); - it != setOfOffsets.constEnd(); ++it ) - { - *ptr = *it; - ptr++; - } + QVector< uint32_t > offsets( setOfOffsets.begin(),setOfOffsets.end() ); + // offsets.resize( setOfOffsets.size() ); + // uint32_t * ptr = &offsets.front(); + // + // for( QSet< uint32_t >::ConstIterator it = setOfOffsets.constBegin(); + // it != setOfOffsets.constEnd(); ++it ) + // { + // *ptr = *it; + // ptr++; + // } setOfOffsets.clear(); @@ -943,16 +951,16 @@ void FTSResultsRequest::fullIndexSearch( BtreeIndexing::BtreeIndex & ftsIndex, allWordsLinks.clear(); - QVector< uint32_t > offsets; - offsets.resize( setOfOffsets.size() ); - uint32_t * ptr = &offsets.front(); - - for( QSet< uint32_t >::ConstIterator it = setOfOffsets.constBegin(); - it != setOfOffsets.constEnd(); ++it ) - { - *ptr = *it; - ptr++; - } + QVector< uint32_t > offsets( setOfOffsets.begin(), setOfOffsets.end() ); + // offsets.resize( setOfOffsets.size() ); + // uint32_t * ptr = &offsets.front(); + // + // for( QSet< uint32_t >::ConstIterator it = setOfOffsets.constBegin(); + // it != setOfOffsets.constEnd(); ++it ) + // { + // *ptr = *it; + // ptr++; + // } setOfOffsets.clear(); From afd477e3a65b055ff7d4eb663c00111a917f7079 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 13:40:37 +0800 Subject: [PATCH 122/125] fix:a very race case boundary protection need further inverstigation --- ftshelpers.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ftshelpers.cc b/ftshelpers.cc index a12325be..6225ff5d 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -453,7 +453,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, QSemaphore sem( parallel_count < 1 ? 1 : parallel_count ); QFutureSynchronizer< void > synchronizer; - auto searchRegularExpression = createMatchRegex( searchRegexp ); + const auto searchRegularExpression = createMatchRegex( searchRegexp ); for( auto & address : offsets ) { @@ -464,7 +464,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, } sem.acquire(); QFuture< void > f = QtConcurrent::run( - [ & ]() + [ =,&sem ]() { QSemaphoreReleaser releaser( sem ); checkSingleArticle( address, words, searchRegularExpression ); @@ -826,8 +826,12 @@ void FTSResultsRequest::combinedIndexSearch( BtreeIndexing::BtreeIndex & ftsInde memcpy( &size, linksPtr, sizeof(uint32_t) ); linksPtr += sizeof(uint32_t); + // across chunks, need further investigation + uint32_t max = ( chunk.size() - ( linksPtr - &chunk.front() )) / 4; + tmp.reserve( size ); - for( uint32_t y = 0; y < size; y++ ) + uint32_t q_max = qMin(size,max); + for( uint32_t y = 0; y < q_max; y++ ) { tmp.insert( *( reinterpret_cast< uint32_t * >( linksPtr ) ) ); linksPtr += sizeof(uint32_t); From a5726aead75ef3199cd91e21e9bb39a9a811ff46 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 18:16:37 +0800 Subject: [PATCH 123/125] replace regularexpress with global static variables --- base/globalregex.cc | 8 ++++++++ base/globalregex.hh | 4 ++++ ftshelpers.cc | 8 +------- mdx.cc | 6 +++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/base/globalregex.cc b/base/globalregex.cc index fab12fe5..a9a6f8fc 100644 --- a/base/globalregex.cc +++ b/base/globalregex.cc @@ -15,6 +15,11 @@ QRegularExpression Ftx::setsRegExp( "\\[[^\\]]+\\]", QRegularExpression::CaseIns QRegularExpression Ftx::regexRegExp( "\\\\[afnrtvdDwWsSbB]|\\\\x([0-9A-Fa-f]{4})|\\\\0([0-7]{3})", QRegularExpression::CaseInsensitiveOption ); +QRegularExpression Ftx::handleRoundBracket( "[^\\w\\(\\)\\p{M}]+" , + QRegularExpression::UseUnicodePropertiesOption ); +QRegularExpression Ftx::noRoundBracket( "[^\\w\\p{M}]+", + QRegularExpression::UseUnicodePropertiesOption ); + //mdx @@ -48,3 +53,6 @@ QRegularExpression Mdx::srcRe( "([\\s\"']src\\s*=)\\s*([\"'])(?!\\s*\\b(?:(?:bre QRegularExpression Mdx::srcRe2( "([\\s\"']src\\s*=)\\s*(?![\\s\"']|\\b(?:(?:bres|https?|ftp)://" "|(?:data|javascript):))(?:file://)?[\\x00-\\x1f\\x7f]*\\.*/?([^\\s\">]+)", QRegularExpression::CaseInsensitiveOption ); + +QRegularExpression Mdx::links( "url\\(\\s*(['\"]?)([^'\"]*)(['\"]?)\\s*\\)", + QRegularExpression::CaseInsensitiveOption ); diff --git a/base/globalregex.hh b/base/globalregex.hh index 012aba0d..783a2be8 100644 --- a/base/globalregex.hh +++ b/base/globalregex.hh @@ -14,6 +14,8 @@ public: static QRegularExpression wordRegExp; static QRegularExpression setsRegExp; static QRegularExpression regexRegExp; + static QRegularExpression handleRoundBracket; + static QRegularExpression noRoundBracket; }; @@ -33,6 +35,8 @@ public: static QRegularExpression closeScriptTagRe; static QRegularExpression srcRe; static QRegularExpression srcRe2; + + static QRegularExpression links; }; } // namespace RX diff --git a/ftshelpers.cc b/ftshelpers.cc index 6225ff5d..29bf509a 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -228,16 +228,10 @@ void parseArticleForFts( uint32_t articleAddress, QString & articleText, if( articleText.isEmpty() ) return; - // QRegularExpression regBrackets( "(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+)(\\([\\w\\p{M}]+\\)){0,1}([\\w\\p{M}]+){0,1}(\\([\\w\\p{M}]+\\)){0,1}", - // QRegularExpression::UseUnicodePropertiesOption); - // QRegularExpression regSplit( "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ); - QStringList articleWords = articleText.normalized( QString::NormalizationForm_C ) - .split( QRegularExpression( handleRoundBrackets ? "[^\\w\\(\\)\\p{M}]+" : "[^\\w\\p{M}]+", - QRegularExpression::UseUnicodePropertiesOption ), + .split( handleRoundBrackets ? RX::Ftx::handleRoundBracket : RX::Ftx::noRoundBracket, Qt::SkipEmptyParts ); - QSet< QString > setOfWords; setOfWords.reserve( articleWords.size() ); diff --git a/mdx.cc b/mdx.cc index 4eaeb40f..43ac6426 100644 --- a/mdx.cc +++ b/mdx.cc @@ -799,14 +799,14 @@ void MddResourceRequest::run() { QString css = QString::fromUtf8( data.data(), data.size() ); - QRegularExpression links( "url\\(\\s*(['\"]?)([^'\"]*)(['\"]?)\\s*\\)", - QRegularExpression::CaseInsensitiveOption ); + // QRegularExpression links( "url\\(\\s*(['\"]?)([^'\"]*)(['\"]?)\\s*\\)", + // QRegularExpression::CaseInsensitiveOption ); QString id = QString::fromUtf8( dict.getId().c_str() ); int pos = 0; QString newCSS; - QRegularExpressionMatchIterator it = links.globalMatch( css ); + QRegularExpressionMatchIterator it = RX::Mdx::links.globalMatch( css ); while ( it.hasNext() ) { QRegularExpressionMatch match = it.next(); From ff5e3b57786fd85ecee07d07d083ea5468039b26 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 18:50:05 +0800 Subject: [PATCH 124/125] feat:optimize fulltext search ,skip unfinished ftx --- fulltextsearch.cc | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/fulltextsearch.cc b/fulltextsearch.cc index cb87124b..311859bc 100644 --- a/fulltextsearch.cc +++ b/fulltextsearch.cc @@ -353,25 +353,13 @@ void FullTextSearchDialog::accept() distanceBetweenWords, hasCJK, ignoreWordsOrder ) ) { -// if( hasCJK && ( mode == WholeWords || mode == PlainText ) ) -// { -// QMessageBox message( QMessageBox::Warning, -// "GoldenDict", -// tr( "CJK symbols in search string are not compatible with search modes \"Whole words\" and \"Plain text\"" ), -// QMessageBox::Ok, -// this ); -// message.exec(); -// } -// else - { - QMessageBox message( QMessageBox::Warning, - "GoldenDict", - tr( "The search line must contains at least one word containing " ) + QMessageBox message( QMessageBox::Warning, + "GoldenDict", + tr( "The search line must contains at least one word containing " ) + QString::number( MinimumWordSize ) + tr( " or more symbols" ), - QMessageBox::Ok, - this ); - message.exec(); - } + QMessageBox::Ok, + this ); + message.exec(); return; } @@ -393,6 +381,10 @@ void FullTextSearchDialog::accept() for( unsigned x = 0; x < activeDicts.size(); ++x ) { + if( !activeDicts[ x ] ->haveFTSIndex()) + { + continue; + } sptr< Dictionary::DataRequest > req = activeDicts[ x ]->getSearchResults( ui.searchLine->text(), mode, From f65d47602abac444034c52453282184134ce8d26 Mon Sep 17 00:00:00 2001 From: Xiao YiFang Date: Sat, 18 Jun 2022 16:24:30 +0800 Subject: [PATCH 125/125] concurrent excepton --- mdx.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/mdx.cc b/mdx.cc index 43ac6426..b096959c 100644 --- a/mdx.cc +++ b/mdx.cc @@ -911,14 +911,20 @@ void MdxDictionary::loadArticle( uint32_t offset, string & articleText, bool noF QString articleId; articleId.setNum( ( quint64 )pRecordInfo, 16 ); - ScopedMemMap compressed( dictFile, recordInfo.compressedBlockPos, recordInfo.compressedBlockSize ); - if ( !compressed.startAddress() ) - throw exCorruptDictionary(); - QByteArray decompressed; - if ( !MdictParser::parseCompressedBlock( recordInfo.compressedBlockSize, ( char * )compressed.startAddress(), - recordInfo.decompressedBlockSize, decompressed ) ) - throw exCorruptDictionary(); + + { + Mutex::Lock _( idxMutex ); + ScopedMemMap compressed( dictFile, recordInfo.compressedBlockPos, recordInfo.compressedBlockSize ); + if( !compressed.startAddress() ) + throw exCorruptDictionary(); + + if( !MdictParser::parseCompressedBlock( recordInfo.compressedBlockSize, + (char *)compressed.startAddress(), + recordInfo.decompressedBlockSize, + decompressed ) ) + throw exCorruptDictionary(); + } QString article = MdictParser::toUtf16( encoding.c_str(), decompressed.constData() + recordInfo.recordOffset,