mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-12-05 00:24:06 +00:00
merge conflict
This commit is contained in:
commit
299bc876b1
|
@ -49,6 +49,7 @@ jobs:
|
||||||
|
|
||||||
- name: install deps on macos
|
- name: install deps on macos
|
||||||
run: |
|
run: |
|
||||||
|
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=TRUE
|
||||||
brew install cmake ninja
|
brew install cmake ninja
|
||||||
brew install automake
|
brew install automake
|
||||||
brew install autoconf
|
brew install autoconf
|
||||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-13]
|
os: [macos-13]
|
||||||
qt_ver: [ 6.5.2,6.6.0 ]
|
qt_ver: [ 6.6.0 ]
|
||||||
qt_arch: [clang_64]
|
qt_arch: [clang_64]
|
||||||
env:
|
env:
|
||||||
targetName: GoldenDict
|
targetName: GoldenDict
|
||||||
|
|
23
.github/workflows/macos-homebrew.yml
vendored
23
.github/workflows/macos-homebrew.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-12,macos-13]
|
os: [macos-12,macos-13]
|
||||||
qt_ver: [ 6.5.2,6.6.0 ]
|
qt_ver: [ 6.6.0,6.6.1 ]
|
||||||
qt_arch: [clang_64]
|
qt_arch: [clang_64]
|
||||||
env:
|
env:
|
||||||
targetName: GoldenDict
|
targetName: GoldenDict
|
||||||
|
@ -42,6 +42,7 @@ jobs:
|
||||||
|
|
||||||
- name: install deps on macos
|
- name: install deps on macos
|
||||||
run: |
|
run: |
|
||||||
|
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=TRUE
|
||||||
brew install cmake ninja
|
brew install cmake ninja
|
||||||
brew install automake
|
brew install automake
|
||||||
brew install autoconf
|
brew install autoconf
|
||||||
|
@ -101,22 +102,16 @@ jobs:
|
||||||
macdeployqt ${targetName}.app -qmldir=. -verbose=1
|
macdeployqt ${targetName}.app -qmldir=. -verbose=1
|
||||||
otool -L GoldenDict.app/Contents/MacOS/GoldenDict
|
otool -L GoldenDict.app/Contents/MacOS/GoldenDict
|
||||||
ls -al GoldenDict.app/Contents/Frameworks
|
ls -al GoldenDict.app/Contents/Frameworks
|
||||||
otool -L GoldenDict.app/Contents/Frameworks/libzim.8.dylib
|
find /usr/local/Cellar -name "libicudata.73.dylib" -exec cp {} GoldenDict.app/Contents/Frameworks/ \;
|
||||||
# otool -L GoldenDict.app/Contents/Frameworks/libav*
|
|
||||||
# otool -L GoldenDict.app/Contents/Frameworks/libjxl.0.8.dylib
|
|
||||||
# cp -r /usr/local/Cellar/icu4c/7*/lib/libicu*.dylib GoldenDict.app/Contents/Frameworks
|
|
||||||
# cp -r /usr/local/Cellar/zstd/1.5.5/lib/libzstd.1.dylib GoldenDict.app/Contents/Frameworks
|
|
||||||
dylibbundler -of -b -x GoldenDict.app/Contents/Frameworks/libzim.8.dylib -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/
|
|
||||||
# dylibbundler -of -b -x GoldenDict.app/Contents/Frameworks/libicu* -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/
|
|
||||||
|
|
||||||
find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libicu*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \;
|
find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -name "libicu*" -exec ls -al {} \;
|
||||||
# dylibbundler -of -x GoldenDict.app/Contents/Frameworks/libavformat.60.dylib -x GoldenDict.app/Contents/Frameworks/libavcodec.60.dylib -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/
|
find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libicu*" -exec ls -al {} \;
|
||||||
# dylibbundler -of -b -x GoldenDict.app/Contents/Frameworks/libjxl.0.8.dylib -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/
|
|
||||||
|
find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libzim*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \;
|
||||||
|
find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -name "libicu*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \;
|
||||||
|
|
||||||
otool -L GoldenDict.app/Contents/Frameworks/libicu*
|
otool -L GoldenDict.app/Contents/Frameworks/libicu*
|
||||||
otool -L GoldenDict.app/Contents/Frameworks/libzim.8.dylib
|
otool -L GoldenDict.app/Contents/Frameworks/libzim*
|
||||||
otool -L GoldenDict.app/Contents/Frameworks/libxapian.30.dylib
|
|
||||||
otool -L GoldenDict.app/Contents/Frameworks/liblzma.5.dylib
|
|
||||||
|
|
||||||
codesign --force --deep -s - GoldenDict.app
|
codesign --force --deep -s - GoldenDict.app
|
||||||
|
|
||||||
|
|
2
.github/workflows/ubuntu-6.2.yml
vendored
2
.github/workflows/ubuntu-6.2.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04]
|
os: [ubuntu-20.04]
|
||||||
qt_ver: [ 6.5.2,6.6.0 ]
|
qt_ver: [ 6.6.0 ]
|
||||||
qt_arch: [gcc_64]
|
qt_arch: [gcc_64]
|
||||||
env:
|
env:
|
||||||
version: 23.11.08
|
version: 23.11.08
|
||||||
|
|
2
.github/workflows/windows-6.x.yml
vendored
2
.github/workflows/windows-6.x.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-2019]
|
os: [windows-2019]
|
||||||
qt_ver: [6.5.2,6.6.0 ]
|
qt_ver: [ 6.6.0,6.6.1 ]
|
||||||
qt_arch: [win64_msvc2019_64]
|
qt_arch: [win64_msvc2019_64]
|
||||||
env:
|
env:
|
||||||
targetName: GoldenDict.exe
|
targetName: GoldenDict.exe
|
||||||
|
|
|
@ -41,9 +41,4 @@
|
||||||
<provides>
|
<provides>
|
||||||
<id>org.goldendict_ng.desktop</id>
|
<id>org.goldendict_ng.desktop</id>
|
||||||
</provides>
|
</provides>
|
||||||
<releases>
|
|
||||||
<release version="23.09.08" date="2023-09-08"/>
|
|
||||||
<release version="23.07.23" date="2023-07-23"/>
|
|
||||||
<release version="23.06.01" date="2023-06-01"/>
|
|
||||||
</releases>
|
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -141,6 +141,7 @@ std::string ArticleMaker::makeHtmlHeader( QString const & word, QString const &
|
||||||
.toStdString();
|
.toStdString();
|
||||||
|
|
||||||
result += R"(<script src="qrc:///scripts/gd-builtin.js"></script>)";
|
result += R"(<script src="qrc:///scripts/gd-builtin.js"></script>)";
|
||||||
|
result += R"(<script src="qrc:///scripts/mark.min.js"></script>)";
|
||||||
|
|
||||||
if ( GlobalBroadcaster::instance()->getPreference()->darkReaderMode ) {
|
if ( GlobalBroadcaster::instance()->getPreference()->darkReaderMode ) {
|
||||||
//only enable this darkmode on modern style.
|
//only enable this darkmode on modern style.
|
||||||
|
@ -674,8 +675,8 @@ void ArticleRequest::bodyFinished()
|
||||||
fmt::format_to( std::back_inserter( head ),
|
fmt::format_to( std::back_inserter( head ),
|
||||||
FMT_COMPILE(
|
FMT_COMPILE(
|
||||||
R"( <div class="gdarticle {0} {1}" id="{2}"
|
R"( <div class="gdarticle {0} {1}" id="{2}"
|
||||||
onClick="gdMakeArticleActive( '{3}', false );"
|
onClick="if(typeof gdMakeArticleActive !='undefined') gdMakeArticleActive( '{3}', false );"
|
||||||
onContextMenu="gdMakeArticleActive( '{3}', false );">)" ),
|
onContextMenu="if(typeof gdMakeArticleActive !='undefined') gdMakeArticleActive( '{3}', false );">)" ),
|
||||||
closePrevSpan ? "" : " gdactivearticle",
|
closePrevSpan ? "" : " gdactivearticle",
|
||||||
collapse ? " gdcollapsedarticle" : "",
|
collapse ? " gdcollapsedarticle" : "",
|
||||||
gdFrom,
|
gdFrom,
|
||||||
|
|
|
@ -35,6 +35,20 @@ bool endsWithIgnoreCase( const string & str1, string str2 )
|
||||||
return ( str1.size() >= (unsigned)str2.size() )
|
return ( str1.size() >= (unsigned)str2.size() )
|
||||||
&& ( strcasecmp( str1.c_str() + ( str1.size() - str2.size() ), str2.data() ) == 0 );
|
&& ( strcasecmp( str1.c_str() + ( str1.size() - str2.size() ), str2.data() ) == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString escapeAmps( QString const & str )
|
||||||
|
{
|
||||||
|
QString result( str );
|
||||||
|
result.replace( "&", "&&" );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString unescapeAmps( QString const & str )
|
||||||
|
{
|
||||||
|
QString result( str );
|
||||||
|
result.replace( "&&", "&" );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|
||||||
QString Utils::Path::combine( const QString & path1, const QString & path2 )
|
QString Utils::Path::combine( const QString & path1, const QString & path2 )
|
||||||
|
|
|
@ -341,6 +341,10 @@ void removeDirectory( QString const & directory );
|
||||||
void removeDirectory( string const & directory );
|
void removeDirectory( string const & directory );
|
||||||
} // namespace Fs
|
} // namespace Fs
|
||||||
|
|
||||||
|
QString escapeAmps( QString const & str );
|
||||||
|
|
||||||
|
QString unescapeAmps( QString const & str );
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|
||||||
#endif // UTILS_HH
|
#endif // UTILS_HH
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
|
#include <QtEndian>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace Dsl {
|
namespace Dsl {
|
||||||
|
@ -832,7 +832,33 @@ DslScanner::DslScanner( string const & fileName ):
|
||||||
bool needExactEncoding = false;
|
bool needExactEncoding = false;
|
||||||
|
|
||||||
QByteArray ba = QByteArray::fromRawData( (const char *)firstBytes, 50 );
|
QByteArray ba = QByteArray::fromRawData( (const char *)firstBytes, 50 );
|
||||||
codec = QTextCodec::codecForUtfText( ba, QTextCodec::codecForName( "UTF-8" ) );
|
codec = QTextCodec::codecForUtfText( ba, nullptr );
|
||||||
|
if ( !codec ) {
|
||||||
|
// the encoding has no bom.
|
||||||
|
// check the first char # (0x23).
|
||||||
|
auto hashTag = 0x0023;
|
||||||
|
|
||||||
|
auto uci = qFromUnaligned< uint32_t >( firstBytes );
|
||||||
|
if ( uci == qToBigEndian( hashTag ) ) {
|
||||||
|
codec = QTextCodec::codecForMib( 1018 ); // utf-32 be
|
||||||
|
}
|
||||||
|
else if ( uci == qToLittleEndian( hashTag ) ) {
|
||||||
|
codec = QTextCodec::codecForMib( 1019 ); // utf-32 le
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto uc = qFromUnaligned< uint16_t >( firstBytes );
|
||||||
|
if ( uc == qToBigEndian( uint16_t( hashTag ) ) ) {
|
||||||
|
codec = QTextCodec::codecForMib( 1013 ); // utf16 be
|
||||||
|
}
|
||||||
|
else if ( uc == qToLittleEndian( uint16_t( hashTag ) ) ) {
|
||||||
|
codec = QTextCodec::codecForMib( 1014 ); // utf16 le
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//default encoding
|
||||||
|
codec = QTextCodec::codecForName( "UTF-8" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
encoding = Utf8::getEncodingForName( codec->name() );
|
encoding = Utf8::getEncodingForName( codec->name() );
|
||||||
qDebug() << codec->name();
|
qDebug() << codec->name();
|
||||||
|
|
|
@ -320,7 +320,7 @@ bool MdictParser::readHeader( QDataStream & in )
|
||||||
|
|
||||||
if ( headerText.contains( "StyleSheet" ) ) {
|
if ( headerText.contains( "StyleSheet" ) ) {
|
||||||
// a workaround to bypass https://bugreports.qt.io/browse/QTBUG-102612
|
// a workaround to bypass https://bugreports.qt.io/browse/QTBUG-102612
|
||||||
QRegularExpression rx( "StyleSheet=\"([^\"]*?)\"", QRegularExpression::CaseInsensitiveOption );
|
QRegularExpression const rx( "StyleSheet=\"([^\"]*?)\"", QRegularExpression::CaseInsensitiveOption );
|
||||||
|
|
||||||
auto match = rx.match( headerText );
|
auto match = rx.match( headerText );
|
||||||
|
|
||||||
|
@ -334,6 +334,9 @@ bool MdictParser::readHeader( QDataStream & in )
|
||||||
|
|
||||||
QDomNamedNodeMap headerAttributes = parseHeaderAttributes( headerText );
|
QDomNamedNodeMap headerAttributes = parseHeaderAttributes( headerText );
|
||||||
|
|
||||||
|
if ( headerAttributes.isEmpty() )
|
||||||
|
return false;
|
||||||
|
|
||||||
encoding_ = headerAttributes.namedItem( "Encoding" ).toAttr().value();
|
encoding_ = headerAttributes.namedItem( "Encoding" ).toAttr().value();
|
||||||
if ( encoding_ == "GBK" || encoding_ == "GB2312" ) {
|
if ( encoding_ == "GBK" || encoding_ == "GB2312" ) {
|
||||||
encoding_ = "GB18030";
|
encoding_ = "GB18030";
|
||||||
|
|
|
@ -31,10 +31,8 @@ bool ftsIndexIsOldOrBad( BtreeIndexing::BtreeDictionary * dict )
|
||||||
auto document = db.get_document( docid );
|
auto document = db.get_document( docid );
|
||||||
|
|
||||||
string const lastDoc = document.get_data();
|
string const lastDoc = document.get_data();
|
||||||
bool const notFinished = lastDoc != finish_mark;
|
return lastDoc != finish_mark;
|
||||||
qDebug() << dict->ftsIndexName().c_str() << document.get_data().c_str() << notFinished;
|
|
||||||
//use a special document to mark the end of the index.
|
//use a special document to mark the end of the index.
|
||||||
return notFinished;
|
|
||||||
}
|
}
|
||||||
catch ( Xapian::Error & e ) {
|
catch ( Xapian::Error & e ) {
|
||||||
qWarning() << e.get_description().c_str();
|
qWarning() << e.get_description().c_str();
|
||||||
|
@ -118,8 +116,9 @@ void makeFTSIndex( BtreeIndexing::BtreeDictionary * dict, QAtomicInt & isCancell
|
||||||
for ( auto const & address : offsets ) {
|
for ( auto const & address : offsets ) {
|
||||||
indexedDoc++;
|
indexedDoc++;
|
||||||
|
|
||||||
if ( address > lastAddress && skip ) {
|
if ( address == lastAddress && skip ) {
|
||||||
skip = false;
|
skip = false;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
//skip until to the lastAddress;
|
//skip until to the lastAddress;
|
||||||
if ( skip ) {
|
if ( skip ) {
|
||||||
|
@ -138,12 +137,8 @@ void makeFTSIndex( BtreeIndexing::BtreeDictionary * dict, QAtomicInt & isCancell
|
||||||
|
|
||||||
indexer.set_document( doc );
|
indexer.set_document( doc );
|
||||||
|
|
||||||
if ( GlobalBroadcaster::instance()->getPreference()->fts.enablePosition ) {
|
indexer.index_text( articleStr.toStdString() );
|
||||||
indexer.index_text( articleStr.toStdString() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
indexer.index_text_without_positions( articleStr.toStdString() );
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.set_data( std::to_string( address ) );
|
doc.set_data( std::to_string( address ) );
|
||||||
// Add the document to the database.
|
// Add the document to the database.
|
||||||
|
@ -199,10 +194,14 @@ void FTSResultsRequest::run()
|
||||||
// Parse the query string to produce a Xapian::Query object.
|
// Parse the query string to produce a Xapian::Query object.
|
||||||
Xapian::QueryParser qp;
|
Xapian::QueryParser qp;
|
||||||
qp.set_database( db );
|
qp.set_database( db );
|
||||||
Xapian::QueryParser::feature_flag flag = Xapian::QueryParser::FLAG_DEFAULT;
|
qp.set_default_op( Xapian::Query::op::OP_AND );
|
||||||
if ( searchMode == FTS::Wildcards )
|
int flag =
|
||||||
flag = Xapian::QueryParser::FLAG_WILDCARD;
|
Xapian::QueryParser::FLAG_DEFAULT | Xapian::QueryParser::FLAG_PURE_NOT | Xapian::QueryParser::FLAG_CJK_NGRAM;
|
||||||
Xapian::Query query = qp.parse_query( query_string, flag | Xapian::QueryParser::FLAG_CJK_NGRAM );
|
if ( searchMode == FTS::Wildcards ) {
|
||||||
|
flag = flag | Xapian::QueryParser::FLAG_WILDCARD;
|
||||||
|
qp.set_max_expansion( 1 );
|
||||||
|
}
|
||||||
|
Xapian::Query query = qp.parse_query( query_string, flag );
|
||||||
qDebug() << "Parsed query is: " << query.get_description().c_str();
|
qDebug() << "Parsed query is: " << query.get_description().c_str();
|
||||||
|
|
||||||
// Find the top 100 results for the query.
|
// Find the top 100 results for the query.
|
||||||
|
|
|
@ -60,7 +60,7 @@ void IframeSchemeHandler::requestStarted( QWebEngineUrlRequestJob * requestJob )
|
||||||
|
|
||||||
QString root = reply->url().scheme() + "://" + reply->url().host();
|
QString root = reply->url().scheme() + "://" + reply->url().host();
|
||||||
|
|
||||||
if ( reply->url().port() != 80 && reply->url().port() != 443 ) {
|
if ( reply->url().port() != 80 && reply->url().port() != 443 && reply->url().port() != -1 ) {
|
||||||
root = root + ":" + QString::number( reply->url().port() );
|
root = root + ":" + QString::number( reply->url().port() );
|
||||||
}
|
}
|
||||||
QString base = root + reply->url().path();
|
QString base = root + reply->url().path();
|
||||||
|
|
|
@ -307,7 +307,7 @@ void processCommandLine( QCoreApplication * app, GDOptions * result )
|
||||||
|
|
||||||
int main( int argc, char ** argv )
|
int main( int argc, char ** argv )
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_UNIX
|
#if defined( Q_OS_UNIX ) && !defined( Q_OS_MACOS )
|
||||||
// GoldenDict use lots of X11 functions and it currently cannot work
|
// GoldenDict use lots of X11 functions and it currently cannot work
|
||||||
// natively on Wayland. This workaround will force GoldenDict to use
|
// natively on Wayland. This workaround will force GoldenDict to use
|
||||||
// XWayland.
|
// XWayland.
|
||||||
|
|
1241
src/scripts/mark.js
Normal file
1241
src/scripts/mark.js
Normal file
File diff suppressed because it is too large
Load diff
13
src/scripts/mark.min.js
vendored
Normal file
13
src/scripts/mark.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -9,5 +9,6 @@
|
||||||
<file>jquery-3.6.0.slim.min.js</file>
|
<file>jquery-3.6.0.slim.min.js</file>
|
||||||
<file>popper.min.js</file>
|
<file>popper.min.js</file>
|
||||||
<file>tippy.min.js</file>
|
<file>tippy.min.js</file>
|
||||||
|
<file>mark.min.js</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <QWebEngineSettings>
|
#include <QWebEngineSettings>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
|
#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
|
||||||
#include <QWebEngineContextMenuData>
|
#include <QWebEngineContextMenuData>
|
||||||
|
@ -477,8 +478,9 @@ void ArticleView::loadFinished( bool result )
|
||||||
if ( result ) {
|
if ( result ) {
|
||||||
emit pageLoaded( this );
|
emit pageLoaded( this );
|
||||||
}
|
}
|
||||||
if ( Utils::Url::hasQueryItem( webview->url(), "regexp" ) )
|
if ( Utils::Url::hasQueryItem( webview->url(), "regexp" ) ) {
|
||||||
highlightFTSResults();
|
highlightFTSResults();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArticleView::handleTitleChanged( QString const & title )
|
void ArticleView::handleTitleChanged( QString const & title )
|
||||||
|
@ -555,10 +557,11 @@ bool ArticleView::setCurrentArticle( QString const & id, bool moveToIt )
|
||||||
QString dictId = id.mid( 7 );
|
QString dictId = id.mid( 7 );
|
||||||
if ( dictId.isEmpty() )
|
if ( dictId.isEmpty() )
|
||||||
return false;
|
return false;
|
||||||
QString script = QString(
|
QString script =
|
||||||
"var elem=document.getElementById('%1'); "
|
QString(
|
||||||
"if(elem!=undefined){elem.scrollIntoView(true);} gdMakeArticleActive('%2',true);" )
|
"var elem=document.getElementById('%1'); "
|
||||||
.arg( id, dictId );
|
"if(elem!=undefined){elem.scrollIntoView(true);} if(typeof gdMakeArticleActive !='undefined') gdMakeArticleActive('%2',true);" )
|
||||||
|
.arg( id, dictId );
|
||||||
onJsActiveArticleChanged( id );
|
onJsActiveArticleChanged( id );
|
||||||
webview->page()->runJavaScript( script );
|
webview->page()->runJavaScript( script );
|
||||||
setActiveArticleId( dictId );
|
setActiveArticleId( dictId );
|
||||||
|
@ -1491,6 +1494,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
|
||||||
QAction * addHeaderToHistoryAction = nullptr;
|
QAction * addHeaderToHistoryAction = nullptr;
|
||||||
QAction * sendWordToInputLineAction = nullptr;
|
QAction * sendWordToInputLineAction = nullptr;
|
||||||
QAction * saveImageAction = nullptr;
|
QAction * saveImageAction = nullptr;
|
||||||
|
QAction * openImageAction = nullptr;
|
||||||
QAction * saveSoundAction = nullptr;
|
QAction * saveSoundAction = nullptr;
|
||||||
QAction * saveBookmark = nullptr;
|
QAction * saveBookmark = nullptr;
|
||||||
|
|
||||||
|
@ -1534,6 +1538,9 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
|
||||||
menu.addAction( webview->pageAction( QWebEnginePage::CopyImageToClipboard ) );
|
menu.addAction( webview->pageAction( QWebEnginePage::CopyImageToClipboard ) );
|
||||||
saveImageAction = new QAction( tr( "Save &image..." ), &menu );
|
saveImageAction = new QAction( tr( "Save &image..." ), &menu );
|
||||||
menu.addAction( saveImageAction );
|
menu.addAction( saveImageAction );
|
||||||
|
|
||||||
|
openImageAction = new QAction( tr( "Open image in system viewer..." ), &menu );
|
||||||
|
menu.addAction( openImageAction );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1746,6 +1753,36 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
|
||||||
saveResource( url, webview->url(), fileName );
|
saveResource( url, webview->url(), fileName );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( result == openImageAction ) {
|
||||||
|
QUrl url = imageUrl;
|
||||||
|
QString fileName;
|
||||||
|
|
||||||
|
|
||||||
|
QString name = Utils::Url::path( url ).section( '/', -1 );
|
||||||
|
// Image data
|
||||||
|
|
||||||
|
// Check for babylon image name
|
||||||
|
if ( name[ 0 ] == '\x1E' )
|
||||||
|
name.remove( 0, 1 );
|
||||||
|
if ( name.length() && name[ name.length() - 1 ] == '\x1F' )
|
||||||
|
name.chop( 1 );
|
||||||
|
|
||||||
|
fileName = QDir::temp().filePath( QString::number( QRandomGenerator::global()->generate() ) + name );
|
||||||
|
|
||||||
|
if ( !fileName.isEmpty() ) {
|
||||||
|
QFileInfo fileInfo( fileName );
|
||||||
|
auto handler = saveResource( url, webview->url(), fileName );
|
||||||
|
|
||||||
|
if ( !handler->isEmpty() ) {
|
||||||
|
connect( handler, &ResourceToSaveHandler::done, this, [ fileName ]() {
|
||||||
|
QDesktopServices::openUrl( fileName );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QDesktopServices::openUrl( fileName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if ( !popupView && result == maxDictionaryRefsAction )
|
if ( !popupView && result == maxDictionaryRefsAction )
|
||||||
emit showDictsPane();
|
emit showDictsPane();
|
||||||
|
@ -2136,54 +2173,21 @@ void ArticleView::highlightFTSResults()
|
||||||
if ( regString.isEmpty() )
|
if ( regString.isEmpty() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//<div><i>watch</i>out</div> 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( R"(\b)" ) );
|
|
||||||
|
|
||||||
//make it simple ,and do not support too much complex cases. such as wildcard etc.
|
//replace any unicode Number ,Symbol ,Punctuation ,Mark character to whitespace
|
||||||
firstAvailableText = regString;
|
regString.replace( QRegularExpression( R"([\p{N}\p{S}\p{P}\p{M}])", QRegularExpression::UseUnicodePropertiesOption ),
|
||||||
|
" " );
|
||||||
|
|
||||||
if ( firstAvailableText.isEmpty() ) {
|
if ( regString.trimmed().isEmpty() )
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove possible wildcard character.
|
|
||||||
auto cleaned =
|
|
||||||
firstAvailableText.split( QRegularExpression( "\\p{P}", QRegularExpression::UseUnicodePropertiesOption ) );
|
|
||||||
|
|
||||||
if ( cleaned.empty() )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
firstAvailableText = cleaned.at( 0 );
|
QString script = QString(
|
||||||
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
|
"var context = document.querySelector(\"body\");\n"
|
||||||
webview->findText( firstAvailableText,
|
"var instance = new Mark(context);\n"
|
||||||
QWebEnginePage::FindBackward,
|
"instance.mark(\"%1\",{\"accuracy\": \"exactly\"});" )
|
||||||
[ & ]( const QWebEngineFindTextResult & result ) {
|
.arg( regString );
|
||||||
qInfo() << result.activeMatch() << "of" << result.numberOfMatches() << "matches";
|
|
||||||
|
|
||||||
if ( result.numberOfMatches() == 0 ) {
|
webview->page()->runJavaScript( script );
|
||||||
ftsSearchPanel->statusLabel->setText( searchStatusMessageNoMatches() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ftsSearchPanel->statusLabel->setText(
|
|
||||||
searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
ftsSearchPanel->show();
|
|
||||||
ftsSearchPanel->previous->setEnabled( result.numberOfMatches() > 1 );
|
|
||||||
ftsSearchPanel->next->setEnabled( result.numberOfMatches() > 1 );
|
|
||||||
|
|
||||||
ftsSearchIsOpened = true;
|
|
||||||
} );
|
|
||||||
#else
|
|
||||||
webview->findText( firstAvailableText, QWebEnginePage::FindBackward, [ this ]( bool res ) {
|
|
||||||
ftsSearchPanel->previous->setEnabled( res );
|
|
||||||
if ( !ftsSearchPanel->next->isEnabled() )
|
|
||||||
ftsSearchPanel->next->setEnabled( res );
|
|
||||||
} );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArticleView::setActiveDictIds( const ActiveDictIds & ad )
|
void ArticleView::setActiveDictIds( const ActiveDictIds & ad )
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "langcoder.hh"
|
#include "langcoder.hh"
|
||||||
#include "language.hh"
|
#include "language.hh"
|
||||||
#include "metadata.hh"
|
#include "metadata.hh"
|
||||||
|
#include "utils.hh"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
@ -517,23 +518,6 @@ DictGroupsWidget::DictGroupsWidget( QWidget * parent ):
|
||||||
setUsesScrollButtons( true );
|
setUsesScrollButtons( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
QString escapeAmps( QString const & str )
|
|
||||||
{
|
|
||||||
QString result( str );
|
|
||||||
result.replace( "&", "&&" );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString unescapeAmps( QString const & str )
|
|
||||||
{
|
|
||||||
QString result( str );
|
|
||||||
result.replace( "&&", "&" );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void DictGroupsWidget::populate( Config::Groups const & groups,
|
void DictGroupsWidget::populate( Config::Groups const & groups,
|
||||||
vector< sptr< Dictionary::Class > > const & allDicts_,
|
vector< sptr< Dictionary::Class > > const & allDicts_,
|
||||||
|
@ -546,7 +530,7 @@ void DictGroupsWidget::populate( Config::Groups const & groups,
|
||||||
|
|
||||||
for ( int x = 0; x < groups.size(); ++x ) {
|
for ( int x = 0; x < groups.size(); ++x ) {
|
||||||
const auto gr = new DictGroupWidget( this, *allDicts, groups[ x ] );
|
const auto gr = new DictGroupWidget( this, *allDicts, groups[ x ] );
|
||||||
addTab( gr, escapeAmps( groups[ x ].name ) );
|
addTab( gr, Utils::escapeAmps( groups[ x ].name ) );
|
||||||
connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo );
|
connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo );
|
||||||
connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
|
connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
|
||||||
|
|
||||||
|
@ -569,7 +553,7 @@ Config::Groups DictGroupsWidget::makeGroups() const
|
||||||
|
|
||||||
for ( int x = 0; x < count(); ++x ) {
|
for ( int x = 0; x < count(); ++x ) {
|
||||||
result.push_back( dynamic_cast< DictGroupWidget & >( *widget( x ) ).makeGroup() );
|
result.push_back( dynamic_cast< DictGroupWidget & >( *widget( x ) ).makeGroup() );
|
||||||
result.back().name = unescapeAmps( tabText( x ) );
|
result.back().name = Utils::unescapeAmps( tabText( x ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -638,7 +622,7 @@ int DictGroupsWidget::addNewGroup( QString const & name )
|
||||||
newGroup.id = nextId++;
|
newGroup.id = nextId++;
|
||||||
|
|
||||||
const auto gr = new DictGroupWidget( this, *allDicts, newGroup );
|
const auto gr = new DictGroupWidget( this, *allDicts, newGroup );
|
||||||
const int idx = insertTab( currentIndex() + 1, gr, escapeAmps( name ) );
|
const int idx = insertTab( currentIndex() + 1, gr, Utils::escapeAmps( name ) );
|
||||||
connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo );
|
connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo );
|
||||||
|
|
||||||
connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
|
connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
|
||||||
|
@ -897,7 +881,7 @@ QString DictGroupsWidget::getCurrentGroupName() const
|
||||||
const int current = currentIndex();
|
const int current = currentIndex();
|
||||||
|
|
||||||
if ( current >= 0 )
|
if ( current >= 0 )
|
||||||
return unescapeAmps( tabText( current ) );
|
return Utils::unescapeAmps( tabText( current ) );
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
@ -907,7 +891,7 @@ void DictGroupsWidget::renameCurrentGroup( QString const & name )
|
||||||
const int current = currentIndex();
|
const int current = currentIndex();
|
||||||
|
|
||||||
if ( current >= 0 )
|
if ( current >= 0 )
|
||||||
setTabText( current, escapeAmps( name ) );
|
setTabText( current, Utils::escapeAmps( name ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void DictGroupsWidget::removeCurrentGroup()
|
void DictGroupsWidget::removeCurrentGroup()
|
||||||
|
|
|
@ -1814,8 +1814,7 @@ ArticleView * MainWindow::createNewTab( bool switchToIt, QString const & name )
|
||||||
|
|
||||||
int index = cfg.preferences.newTabsOpenAfterCurrentOne ? ui.tabWidget->currentIndex() + 1 : ui.tabWidget->count();
|
int index = cfg.preferences.newTabsOpenAfterCurrentOne ? ui.tabWidget->currentIndex() + 1 : ui.tabWidget->count();
|
||||||
|
|
||||||
QString escaped = name;
|
QString escaped = Utils::escapeAmps( name );
|
||||||
escaped.replace( "&", "&&" );
|
|
||||||
|
|
||||||
ui.tabWidget->insertTab( index, view, escaped );
|
ui.tabWidget->insertTab( index, view, escaped );
|
||||||
mruList.append( dynamic_cast< QWidget * >( view ) );
|
mruList.append( dynamic_cast< QWidget * >( view ) );
|
||||||
|
@ -1943,7 +1942,7 @@ void MainWindow::titleChanged( ArticleView * view, QString const & title )
|
||||||
else {
|
else {
|
||||||
escaped = title;
|
escaped = title;
|
||||||
}
|
}
|
||||||
escaped.replace( "&", "&&" );
|
escaped = Utils::escapeAmps( escaped );
|
||||||
|
|
||||||
int index = ui.tabWidget->indexOf( view );
|
int index = ui.tabWidget->indexOf( view );
|
||||||
if ( !escaped.isEmpty() )
|
if ( !escaped.isEmpty() )
|
||||||
|
@ -2856,7 +2855,7 @@ void MainWindow::toggleMainWindow( bool onlyShow )
|
||||||
|
|
||||||
void MainWindow::installHotKeys()
|
void MainWindow::installHotKeys()
|
||||||
{
|
{
|
||||||
#if defined( Q_OS_LINUX )
|
#if defined( Q_OS_UNIX ) && !defined( Q_OS_MACOS )
|
||||||
if ( !qEnvironmentVariableIsEmpty( "GOLDENDICT_FORCE_WAYLAND" ) ) {
|
if ( !qEnvironmentVariableIsEmpty( "GOLDENDICT_FORCE_WAYLAND" ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4218,7 +4217,7 @@ void MainWindow::showFullTextSearchDialog()
|
||||||
{
|
{
|
||||||
if ( !ftsDlg ) {
|
if ( !ftsDlg ) {
|
||||||
ftsDlg = new FTS::FullTextSearchDialog( this, cfg, dictionaries, groupInstances, ftsIndexing );
|
ftsDlg = new FTS::FullTextSearchDialog( this, cfg, dictionaries, groupInstances, ftsIndexing );
|
||||||
ftsDlg->setSearchText( translateLine->text() );
|
// ftsDlg->setSearchText( translateLine->text() );
|
||||||
|
|
||||||
addGlobalActionsToDialog( ftsDlg );
|
addGlobalActionsToDialog( ftsDlg );
|
||||||
addGroupComboBoxActionsToDialog( ftsDlg, groupList );
|
addGroupComboBoxActionsToDialog( ftsDlg, groupList );
|
||||||
|
@ -4264,11 +4263,7 @@ void MainWindow::showFTSIndexingName( QString const & name )
|
||||||
QString MainWindow::unescapeTabHeader( QString const & header )
|
QString MainWindow::unescapeTabHeader( QString const & header )
|
||||||
{
|
{
|
||||||
// Reset table header to original headword
|
// Reset table header to original headword
|
||||||
|
return Utils::unescapeAmps( header );
|
||||||
QString escaped = header;
|
|
||||||
escaped.replace( "&&", "&" );
|
|
||||||
|
|
||||||
return escaped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addCurrentTabToFavorites()
|
void MainWindow::addCurrentTabToFavorites()
|
||||||
|
|
|
@ -376,6 +376,7 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
|
||||||
ui.allowGls->setChecked( !p.fts.disabledTypes.contains( "GLS", Qt::CaseInsensitive ) );
|
ui.allowGls->setChecked( !p.fts.disabledTypes.contains( "GLS", Qt::CaseInsensitive ) );
|
||||||
|
|
||||||
ui.enablePosition->setChecked( p.fts.enablePosition );
|
ui.enablePosition->setChecked( p.fts.enablePosition );
|
||||||
|
ui.enablePosition->hide();
|
||||||
#ifndef MAKE_ZIM_SUPPORT
|
#ifndef MAKE_ZIM_SUPPORT
|
||||||
ui.allowZim->hide();
|
ui.allowZim->hide();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,7 +44,7 @@ nav:
|
||||||
- Custom transliteration: topic_transliteration.md
|
- Custom transliteration: topic_transliteration.md
|
||||||
- Customize Dictionary: custom_dictionary.md
|
- Customize Dictionary: custom_dictionary.md
|
||||||
- OCR Integration: howto/ocr.md
|
- OCR Integration: howto/ocr.md
|
||||||
- Wayland/Linux: topic_wayland.md
|
- Wayland: topic_wayland.md
|
||||||
- Report Bugs & Feedbacks: feedbacks.md
|
- Report Bugs & Feedbacks: feedbacks.md
|
||||||
- Contributor Guides:
|
- Contributor Guides:
|
||||||
- Developer: developer.md
|
- Developer: developer.md
|
||||||
|
|
Loading…
Reference in a new issue