mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-12-18 11:34:05 +00:00
Merge branch 'staged' into dev
Some checks are pending
Release All / build_macOS (macos-13, 6.6.3) (push) Waiting to run
Release All / build_macOS (macos-13, 6.7.2) (push) Waiting to run
Release All / build_macOS (macos-14, 6.6.3) (push) Waiting to run
Release All / build_macOS (macos-14, 6.7.2) (push) Waiting to run
Release All / build_Windows (windows-2022, 6.6.3) (push) Waiting to run
Release All / build_Windows (windows-2022, 6.7.2) (push) Waiting to run
Release All / generate_other_staffs (push) Waiting to run
Release All / publish (push) Blocked by required conditions
Some checks are pending
Release All / build_macOS (macos-13, 6.6.3) (push) Waiting to run
Release All / build_macOS (macos-13, 6.7.2) (push) Waiting to run
Release All / build_macOS (macos-14, 6.6.3) (push) Waiting to run
Release All / build_macOS (macos-14, 6.7.2) (push) Waiting to run
Release All / build_Windows (windows-2022, 6.6.3) (push) Waiting to run
Release All / build_Windows (windows-2022, 6.7.2) (push) Waiting to run
Release All / generate_other_staffs (push) Waiting to run
Release All / publish (push) Blocked by required conditions
This commit is contained in:
commit
7f62fc87c4
4
.github/workflows/PR-check-cmake.yml
vendored
4
.github/workflows/PR-check-cmake.yml
vendored
|
@ -56,7 +56,7 @@ jobs:
|
||||||
brew update
|
brew update
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install \
|
brew install --force --overwrite \
|
||||||
ninja \
|
ninja \
|
||||||
opencc \
|
opencc \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
|
@ -68,7 +68,7 @@ jobs:
|
||||||
hunspell \
|
hunspell \
|
||||||
xapian \
|
xapian \
|
||||||
libzim \
|
libzim \
|
||||||
qt
|
qt || true
|
||||||
- name: Install eb
|
- name: Install eb
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/mistydemeo/eb/releases/download/v4.4.3/eb-4.4.3.tar.bz2
|
wget https://github.com/mistydemeo/eb/releases/download/v4.4.3/eb-4.4.3.tar.bz2
|
||||||
|
|
4
.github/workflows/Release-all.yml
vendored
4
.github/workflows/Release-all.yml
vendored
|
@ -31,7 +31,7 @@ jobs:
|
||||||
brew update
|
brew update
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install \
|
brew install --force --overwrite \
|
||||||
bzip2 \
|
bzip2 \
|
||||||
create-dmg \
|
create-dmg \
|
||||||
hunspell \
|
hunspell \
|
||||||
|
@ -42,7 +42,7 @@ jobs:
|
||||||
lzip \
|
lzip \
|
||||||
ninja \
|
ninja \
|
||||||
opencc \
|
opencc \
|
||||||
xapian
|
xapian || true
|
||||||
- name: Install eb
|
- name: Install eb
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/xiaoyifang/eb.git
|
git clone https://github.com/xiaoyifang/eb.git
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 8.1 KiB |
32
src/common/dictionary_icon_name.cc
Normal file
32
src/common/dictionary_icon_name.cc
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include "dictionary_icon_name.hh"
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
|
|
||||||
|
QString Icons::DictionaryIconName::getIconName( const QString & dictionaryName )
|
||||||
|
{
|
||||||
|
if ( dictionaryName.isEmpty() ) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
QMutexLocker _( &_mutex );
|
||||||
|
|
||||||
|
auto it = _dictionaryIconNames.contains( dictionaryName );
|
||||||
|
if ( it ) {
|
||||||
|
return _dictionaryIconNames.value( dictionaryName );
|
||||||
|
}
|
||||||
|
//get the first character of the dictionary name
|
||||||
|
QString name = dictionaryName.at( 0 ).toUpper();
|
||||||
|
auto it1 = _iconDictionaryNames.contains( name );
|
||||||
|
std::vector< QString > vector = {};
|
||||||
|
if ( it1 ) {
|
||||||
|
vector = _iconDictionaryNames.value( name );
|
||||||
|
vector.emplace_back( dictionaryName );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vector.emplace_back( dictionaryName );
|
||||||
|
_iconDictionaryNames.insert( name, vector );
|
||||||
|
}
|
||||||
|
|
||||||
|
name = name + QString::number( vector.size() );
|
||||||
|
_dictionaryIconNames.insert( dictionaryName, name );
|
||||||
|
return name;
|
||||||
|
}
|
24
src/common/dictionary_icon_name.hh
Normal file
24
src/common/dictionary_icon_name.hh
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <QMap>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <QString>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
namespace Icons {
|
||||||
|
//use dictionary name's (first character + the order number) to represent the dictionary name in the icon image.
|
||||||
|
class DictionaryIconName
|
||||||
|
{
|
||||||
|
//map icon name to dictionary names;
|
||||||
|
QMap< QString, std::vector< QString > > _iconDictionaryNames;
|
||||||
|
//map dictionary name to icon name;
|
||||||
|
QMap< QString, QString > _dictionaryIconNames;
|
||||||
|
|
||||||
|
QMutex _mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString getIconName( const QString & dictionaryName );
|
||||||
|
};
|
||||||
|
} // namespace Icons
|
|
@ -155,8 +155,7 @@ std::u32string applyWhitespaceAndPunctOnly( std::u32string const & in )
|
||||||
|
|
||||||
bool isWhitespace( char32_t ch )
|
bool isWhitespace( char32_t ch )
|
||||||
{
|
{
|
||||||
//invisible character should be treated as whitespace as well.
|
return QChar::isSpace( ch );
|
||||||
return QChar::isSpace( ch ) || !QChar::isPrint( ch );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWhitespaceOrPunct( char32_t ch )
|
bool isWhitespaceOrPunct( char32_t ch )
|
||||||
|
|
|
@ -38,4 +38,22 @@ bool GlobalBroadcaster::existedInWhitelist( QString url ) const
|
||||||
{
|
{
|
||||||
return whitelist.contains( url );
|
return whitelist.contains( url );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString GlobalBroadcaster::getAbbrName( QString const & text )
|
||||||
|
{
|
||||||
|
if ( text.isEmpty() ) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
//remove whitespace,number,mark,puncuation,symbol
|
||||||
|
QString simplified = text;
|
||||||
|
simplified.remove(
|
||||||
|
QRegularExpression( R"([\p{Z}\p{N}\p{M}\p{P}\p{S}])", QRegularExpression::UseUnicodePropertiesOption ) );
|
||||||
|
|
||||||
|
if ( simplified.isEmpty() ) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return _icon_names.getIconName( simplified );
|
||||||
|
}
|
||||||
// namespace global
|
// namespace global
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "config.hh"
|
#include "config.hh"
|
||||||
#include "pronounceengine.hh"
|
#include "pronounceengine.hh"
|
||||||
#include <QCache>
|
#include <QCache>
|
||||||
|
#include "dictionary_icon_name.hh"
|
||||||
|
|
||||||
struct ActiveDictIds
|
struct ActiveDictIds
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,7 @@ class GlobalBroadcaster: public QObject
|
||||||
|
|
||||||
Config::Preferences * preference;
|
Config::Preferences * preference;
|
||||||
QSet< QString > whitelist;
|
QSet< QString > whitelist;
|
||||||
|
Icons::DictionaryIconName _icon_names;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setPreference( Config::Preferences * _pre );
|
void setPreference( Config::Preferences * _pre );
|
||||||
|
@ -40,6 +42,7 @@ public:
|
||||||
QMap< QString, QSet< QString > > folderFavoritesMap;
|
QMap< QString, QSet< QString > > folderFavoritesMap;
|
||||||
QMap< unsigned, QString > groupFolderMap;
|
QMap< unsigned, QString > groupFolderMap;
|
||||||
PronounceEngine pronounce_engine;
|
PronounceEngine pronounce_engine;
|
||||||
|
QString getAbbrName( QString const & text );
|
||||||
signals:
|
signals:
|
||||||
void dictionaryChanges( ActiveDictIds ad );
|
void dictionaryChanges( ActiveDictIds ad );
|
||||||
void dictionaryClear( ActiveDictIds ad );
|
void dictionaryClear( ActiveDictIds ad );
|
||||||
|
|
|
@ -71,8 +71,8 @@ const static QRegularExpression accentMark( R"(\p{M})", QRegularExpression::UseU
|
||||||
//contain unicode space mark,invisible, and punctuation
|
//contain unicode space mark,invisible, and punctuation
|
||||||
const static QRegularExpression markPuncSpace( R"([\p{M}\p{Z}\p{C}\p{P}])",
|
const static QRegularExpression markPuncSpace( R"([\p{M}\p{Z}\p{C}\p{P}])",
|
||||||
QRegularExpression::UseUnicodePropertiesOption );
|
QRegularExpression::UseUnicodePropertiesOption );
|
||||||
//contain unicode space and mark.invisible
|
//contain unicode space and mark.
|
||||||
const static QRegularExpression markSpace( R"([\p{M}\p{Z}\p{C}])", QRegularExpression::UseUnicodePropertiesOption );
|
const static QRegularExpression markSpace( R"([\p{M}\p{Z}])", QRegularExpression::UseUnicodePropertiesOption );
|
||||||
|
|
||||||
const static QRegularExpression whiteSpace( "\\s+" );
|
const static QRegularExpression whiteSpace( "\\s+" );
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,19 @@ inline QString rstrip( const QString & str )
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint32_t leadingSpaceCount( const QString & str )
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < str.size(); i++ ) {
|
||||||
|
if ( str.at( i ).isSpace() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::string c_string( const QString & str );
|
std::string c_string( const QString & str );
|
||||||
bool endsWithIgnoreCase( QByteArrayView str, QByteArrayView extension );
|
bool endsWithIgnoreCase( QByteArrayView str, QByteArrayView extension );
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include "utils.hh"
|
#include "utils.hh"
|
||||||
#include "zipfile.hh"
|
#include "zipfile.hh"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace Dictionary {
|
namespace Dictionary {
|
||||||
|
|
||||||
|
@ -291,7 +292,7 @@ bool Class::loadIconFromFile( QString const & _filename, bool isFullName )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Class::loadIconFromText( QString iconUrl, QString const & text )
|
bool Class::loadIconFromText( const QString & iconUrl, QString const & text )
|
||||||
{
|
{
|
||||||
if ( text.isEmpty() ) {
|
if ( text.isEmpty() ) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -308,7 +309,7 @@ bool Class::loadIconFromText( QString iconUrl, QString const & text )
|
||||||
painter.setCompositionMode( QPainter::CompositionMode_SourceAtop );
|
painter.setCompositionMode( QPainter::CompositionMode_SourceAtop );
|
||||||
|
|
||||||
QFont font = painter.font();
|
QFont font = painter.font();
|
||||||
//the text should be a little smaller than the icon
|
//the orderNum should be a little smaller than the icon
|
||||||
font.setPixelSize( iconSize * 0.6 );
|
font.setPixelSize( iconSize * 0.6 );
|
||||||
font.setWeight( QFont::Bold );
|
font.setWeight( QFont::Bold );
|
||||||
painter.setFont( font );
|
painter.setFont( font );
|
||||||
|
@ -318,8 +319,21 @@ bool Class::loadIconFromText( QString iconUrl, QString const & text )
|
||||||
//select a single char.
|
//select a single char.
|
||||||
auto abbrName = getAbbrName( text );
|
auto abbrName = getAbbrName( text );
|
||||||
|
|
||||||
painter.setPen( QColor( 4, 57, 108, 200 ) );
|
painter.setPen( intToFixedColor( qHash( abbrName ) ) );
|
||||||
painter.drawText( rectangle, Qt::AlignCenter, abbrName );
|
|
||||||
|
// Draw first character
|
||||||
|
painter.drawText( rectangle, Qt::AlignCenter, abbrName.at( 0 ) );
|
||||||
|
|
||||||
|
//the orderNum should be a little smaller than the icon
|
||||||
|
font.setPixelSize( iconSize * 0.4 );
|
||||||
|
QFontMetrics fm1( font );
|
||||||
|
const QString & orderNum = abbrName.mid( 1 );
|
||||||
|
int orderNumberWidth = fm1.horizontalAdvance( orderNum );
|
||||||
|
|
||||||
|
painter.setFont( font );
|
||||||
|
painter.drawText( rectangle.x() + rectangle.width() - orderNumberWidth * 1.2,
|
||||||
|
rectangle.y() + rectangle.height(),
|
||||||
|
orderNum );
|
||||||
|
|
||||||
painter.end();
|
painter.end();
|
||||||
|
|
||||||
|
@ -330,35 +344,30 @@ bool Class::loadIconFromText( QString iconUrl, QString const & text )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QColor Class::intToFixedColor( int index )
|
||||||
|
{
|
||||||
|
// Predefined list of colors
|
||||||
|
static const std::array colors = {
|
||||||
|
QColor( 255, 0, 0, 200 ), // Red
|
||||||
|
QColor( 4, 57, 108, 200 ), //Custom
|
||||||
|
QColor( 0, 255, 0, 200 ), // Green
|
||||||
|
QColor( 0, 0, 255, 200 ), // Blue
|
||||||
|
QColor( 255, 255, 0, 200 ), // Yellow
|
||||||
|
QColor( 0, 255, 255, 200 ), // Cyan
|
||||||
|
QColor( 255, 0, 255, 200 ), // Magenta
|
||||||
|
QColor( 192, 192, 192, 200 ), // Gray
|
||||||
|
QColor( 255, 165, 0, 200 ), // Orange
|
||||||
|
QColor( 128, 0, 128, 200 ), // Violet
|
||||||
|
QColor( 128, 128, 0, 200 ) // Olive
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use modulo operation to ensure index is within the range of the color list
|
||||||
|
return colors[ index % colors.size() ];
|
||||||
|
}
|
||||||
|
|
||||||
QString Class::getAbbrName( QString const & text )
|
QString Class::getAbbrName( QString const & text )
|
||||||
{
|
{
|
||||||
if ( text.isEmpty() ) {
|
return GlobalBroadcaster::instance()->getAbbrName( text );
|
||||||
return {};
|
|
||||||
}
|
|
||||||
//remove whitespace,number,mark,puncuation,symbol
|
|
||||||
QString simplified = text;
|
|
||||||
simplified.remove(
|
|
||||||
QRegularExpression( R"([\p{Z}\p{N}\p{M}\p{P}\p{S}])", QRegularExpression::UseUnicodePropertiesOption ) );
|
|
||||||
|
|
||||||
if ( simplified.isEmpty() ) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
int index = qHash( simplified ) % simplified.size();
|
|
||||||
|
|
||||||
QString abbrName;
|
|
||||||
if ( !Utils::isCJKChar( simplified.at( index ).unicode() ) ) {
|
|
||||||
// take two chars.
|
|
||||||
abbrName = simplified.mid( index, 2 );
|
|
||||||
if ( abbrName.size() == 1 ) {
|
|
||||||
//make up two characters.
|
|
||||||
abbrName = abbrName + simplified.at( 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
abbrName = simplified.mid( index, 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return abbrName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Class::isolateCSS( QString & css, QString const & wrapperSelector )
|
void Class::isolateCSS( QString & css, QString const & wrapperSelector )
|
||||||
|
|
|
@ -318,10 +318,10 @@ protected:
|
||||||
// Load icon from filename directly if isFullName == true
|
// Load icon from filename directly if isFullName == true
|
||||||
// else treat filename as name without extension
|
// else treat filename as name without extension
|
||||||
bool loadIconFromFile( QString const & filename, bool isFullName = false );
|
bool loadIconFromFile( QString const & filename, bool isFullName = false );
|
||||||
bool loadIconFromText( QString iconUrl, QString const & text );
|
bool loadIconFromText( const QString & iconUrl, QString const & text );
|
||||||
|
|
||||||
QString getAbbrName( QString const & text );
|
|
||||||
|
|
||||||
|
static QString getAbbrName( QString const & text );
|
||||||
|
static QColor intToFixedColor( int index );
|
||||||
/// Make css content usable only for articles from this dictionary
|
/// Make css content usable only for articles from this dictionary
|
||||||
void isolateCSS( QString & css, QString const & wrapperSelector = QString() );
|
void isolateCSS( QString & css, QString const & wrapperSelector = QString() );
|
||||||
|
|
||||||
|
|
|
@ -592,12 +592,49 @@ public:
|
||||||
cancel();
|
cancel();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
connect( this, &DictServerArticleRequest::finishedArticle, this, [ this ]( QString articleText ) {
|
connect( this, &DictServerArticleRequest::finishedArticle, this, [ this ]( QString _articleText ) {
|
||||||
if ( Utils::AtomicInt::loadAcquire( isCancelled ) ) {
|
if ( Utils::AtomicInt::loadAcquire( isCancelled ) ) {
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//modify the _articleText,remove extra lines[start with 15X etc.]
|
||||||
|
QList< QString > lines = _articleText.split( "\n", Qt::SkipEmptyParts );
|
||||||
|
|
||||||
|
QString resultStr;
|
||||||
|
|
||||||
|
// process the line
|
||||||
|
static QRegularExpression leadingRespCode( "^\\d{3} " );
|
||||||
|
uint32_t leadingSpaceCount = 0;
|
||||||
|
uint32_t firstLeadingSpaceCount = 0;
|
||||||
|
for ( const QString & line : lines ) {
|
||||||
|
//ignore 15X lines
|
||||||
|
if ( leadingRespCode.match( line ).hasMatch() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ignore dot(.),the end line character
|
||||||
|
if ( line.trimmed() == "." ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lsc = Utils::leadingSpaceCount( line );
|
||||||
|
|
||||||
|
if ( firstLeadingSpaceCount == 0 && lsc > firstLeadingSpaceCount ) {
|
||||||
|
firstLeadingSpaceCount = lsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( lsc >= leadingSpaceCount && lsc > firstLeadingSpaceCount ) {
|
||||||
|
//extra space
|
||||||
|
resultStr.append( " " );
|
||||||
|
resultStr.append( line.trimmed() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultStr.append( "\n" );
|
||||||
|
resultStr.append( line );
|
||||||
|
}
|
||||||
|
leadingSpaceCount = lsc;
|
||||||
|
}
|
||||||
|
|
||||||
static QRegularExpression phonetic( R"(\\([^\\]+)\\)",
|
static QRegularExpression phonetic( R"(\\([^\\]+)\\)",
|
||||||
QRegularExpression::CaseInsensitiveOption ); // phonetics: \stuff\ ...
|
QRegularExpression::CaseInsensitiveOption ); // phonetics: \stuff\ ...
|
||||||
static QRegularExpression divs_inside_phonetic( "</div([^>]*)><div([^>]*)>",
|
static QRegularExpression divs_inside_phonetic( "</div([^>]*)><div([^>]*)>",
|
||||||
|
@ -610,26 +647,26 @@ public:
|
||||||
|
|
||||||
string articleStr;
|
string articleStr;
|
||||||
if ( contentInHtml ) {
|
if ( contentInHtml ) {
|
||||||
articleStr = articleText.toUtf8().data();
|
articleStr = resultStr.toUtf8().data();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
articleStr = Html::preformat( articleText.toUtf8().data() );
|
articleStr = Html::preformat( resultStr.toUtf8().data() );
|
||||||
}
|
}
|
||||||
|
|
||||||
articleText = QString::fromUtf8( articleStr.c_str(), articleStr.size() );
|
_articleText = QString::fromUtf8( articleStr.c_str(), articleStr.size() );
|
||||||
int pos;
|
int pos;
|
||||||
if ( !contentInHtml ) {
|
if ( !contentInHtml ) {
|
||||||
articleText = articleText.replace( refs, R"(<a href="gdlookup://localhost/\1">\1</a>)" );
|
_articleText = _articleText.replace( refs, R"(<a href="gdlookup://localhost/\1">\1</a>)" );
|
||||||
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
QString articleNewText;
|
QString articleNewText;
|
||||||
|
|
||||||
// Handle phonetics
|
// Handle phonetics
|
||||||
|
|
||||||
QRegularExpressionMatchIterator it = phonetic.globalMatch( articleText );
|
QRegularExpressionMatchIterator it = phonetic.globalMatch( _articleText );
|
||||||
while ( it.hasNext() ) {
|
while ( it.hasNext() ) {
|
||||||
QRegularExpressionMatch match = it.next();
|
QRegularExpressionMatch match = it.next();
|
||||||
articleNewText += articleText.mid( pos, match.capturedStart() - pos );
|
articleNewText += _articleText.mid( pos, match.capturedStart() - pos );
|
||||||
pos = match.capturedEnd();
|
pos = match.capturedEnd();
|
||||||
|
|
||||||
QString phonetic_text = match.captured( 1 );
|
QString phonetic_text = match.captured( 1 );
|
||||||
|
@ -638,18 +675,18 @@ public:
|
||||||
articleNewText += R"(<span class="dictd_phonetic">)" + phonetic_text + "</span>";
|
articleNewText += R"(<span class="dictd_phonetic">)" + phonetic_text + "</span>";
|
||||||
}
|
}
|
||||||
if ( pos ) {
|
if ( pos ) {
|
||||||
articleNewText += articleText.mid( pos );
|
articleNewText += _articleText.mid( pos );
|
||||||
articleText = articleNewText;
|
_articleText = articleNewText;
|
||||||
articleNewText.clear();
|
articleNewText.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle links
|
// Handle links
|
||||||
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
it = links.globalMatch( articleText );
|
it = links.globalMatch( _articleText );
|
||||||
while ( it.hasNext() ) {
|
while ( it.hasNext() ) {
|
||||||
QRegularExpressionMatch match = it.next();
|
QRegularExpressionMatch match = it.next();
|
||||||
articleNewText += articleText.mid( pos, match.capturedStart() - pos );
|
articleNewText += _articleText.mid( pos, match.capturedStart() - pos );
|
||||||
pos = match.capturedEnd();
|
pos = match.capturedEnd();
|
||||||
|
|
||||||
QString link = match.captured( 1 );
|
QString link = match.captured( 1 );
|
||||||
|
@ -663,13 +700,13 @@ public:
|
||||||
articleNewText += newLink;
|
articleNewText += newLink;
|
||||||
}
|
}
|
||||||
if ( pos ) {
|
if ( pos ) {
|
||||||
articleNewText += articleText.mid( pos );
|
articleNewText += _articleText.mid( pos );
|
||||||
articleText = articleNewText;
|
_articleText = articleNewText;
|
||||||
articleNewText.clear();
|
articleNewText.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
articleData += string( "<div class=\"dictd_article\">" ) + articleText.toUtf8().data() + "<br></div>";
|
articleData += string( "<div class=\"dictd_article\">" ) + _articleText.toUtf8().data() + "<br></div>";
|
||||||
|
|
||||||
|
|
||||||
if ( !articleData.empty() ) {
|
if ( !articleData.empty() ) {
|
||||||
|
|
|
@ -877,8 +877,7 @@ QString & MdxDictionary::filterResource( QString & article )
|
||||||
void MdxDictionary::replaceLinks( QString & id, QString & article )
|
void MdxDictionary::replaceLinks( QString & id, QString & article )
|
||||||
{
|
{
|
||||||
QString articleNewText;
|
QString articleNewText;
|
||||||
qsizetype linkPos = 0;
|
int linkPos = 0;
|
||||||
|
|
||||||
QRegularExpressionMatchIterator it = RX::Mdx::allLinksRe.globalMatch( article );
|
QRegularExpressionMatchIterator it = RX::Mdx::allLinksRe.globalMatch( article );
|
||||||
while ( it.hasNext() ) {
|
while ( it.hasNext() ) {
|
||||||
QRegularExpressionMatch allLinksMatch = it.next();
|
QRegularExpressionMatch allLinksMatch = it.next();
|
||||||
|
@ -954,14 +953,13 @@ void MdxDictionary::replaceLinks( QString & id, QString & article )
|
||||||
articleNewText += linkTxt;
|
articleNewText += linkTxt;
|
||||||
match = RX::Mdx::closeScriptTagRe.match( article, linkPos );
|
match = RX::Mdx::closeScriptTagRe.match( article, linkPos );
|
||||||
if ( match.hasMatch() ) {
|
if ( match.hasMatch() ) {
|
||||||
articleNewText += QString( QStringLiteral( "gdOnReady(()=>{%1});</script>" ) )
|
articleNewText += article.mid( linkPos, match.capturedEnd() - linkPos );
|
||||||
.arg( article.mid( linkPos, match.capturedStart() - linkPos ) );
|
|
||||||
linkPos = match.capturedEnd();
|
linkPos = match.capturedEnd();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//audio ,script,video ,html5 tags fall here.
|
//audio ,video ,html5 tags fall here.
|
||||||
match = RX::Mdx::srcRe.match( linkTxt );
|
match = RX::Mdx::srcRe.match( linkTxt );
|
||||||
if ( match.hasMatch() ) {
|
if ( match.hasMatch() ) {
|
||||||
QString newText;
|
QString newText;
|
||||||
|
@ -973,15 +971,9 @@ void MdxDictionary::replaceLinks( QString & id, QString & article )
|
||||||
else {
|
else {
|
||||||
scheme = "bres://";
|
scheme = "bres://";
|
||||||
}
|
}
|
||||||
|
|
||||||
newText =
|
newText =
|
||||||
match.captured( 1 ) + match.captured( 2 ) + scheme + id + "/" + match.captured( 3 ) + match.captured( 2 );
|
match.captured( 1 ) + match.captured( 2 ) + scheme + id + "/" + match.captured( 3 ) + match.captured( 2 );
|
||||||
|
|
||||||
//add defer to script tag
|
|
||||||
if ( linkType.compare( "script" ) == 0 ) {
|
|
||||||
newText = newText + " defer ";
|
|
||||||
}
|
|
||||||
|
|
||||||
newLink = linkTxt.replace( match.capturedStart(), match.capturedLength(), newText );
|
newLink = linkTxt.replace( match.capturedStart(), match.capturedLength(), newText );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -275,14 +275,18 @@ quint32 LangCoder::guessId( const QString & lang )
|
||||||
|
|
||||||
std::pair< quint32, quint32 > LangCoder::findLangIdPairFromName( QString const & name )
|
std::pair< quint32, quint32 > LangCoder::findLangIdPairFromName( QString const & name )
|
||||||
{
|
{
|
||||||
static QRegularExpression reg( "(?=([a-z]{2,3})-([a-z]{2,3}))", QRegularExpression::CaseInsensitiveOption );
|
static QRegularExpression reg( "(^|[^a-z])((?<lang1>[a-z]{2,3})-(?<lang2>[a-z]{2,3}))($|[^a-z])",
|
||||||
|
QRegularExpression::CaseInsensitiveOption );
|
||||||
|
|
||||||
auto matches = reg.globalMatch( name );
|
auto matches = reg.globalMatch( name );
|
||||||
while ( matches.hasNext() ) {
|
while ( matches.hasNext() ) {
|
||||||
auto m = matches.next();
|
auto m = matches.next();
|
||||||
|
if ( matches.hasNext() ) {
|
||||||
|
continue; // We use only the last match, skip previous ones
|
||||||
|
}
|
||||||
|
|
||||||
auto fromId = guessId( m.captured( 1 ).toLower() );
|
auto fromId = guessId( m.captured( "lang1" ).toLower() );
|
||||||
auto toId = guessId( m.captured( 2 ).toLower() );
|
auto toId = guessId( m.captured( "lang2" ).toLower() );
|
||||||
|
|
||||||
if ( code2Exists( intToCode2( fromId ) ) && code2Exists( intToCode2( toId ) ) ) {
|
if ( code2Exists( intToCode2( fromId ) ) && code2Exists( intToCode2( toId ) ) ) {
|
||||||
return { fromId, toId };
|
return { fromId, toId };
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
function gdOnReady(func) {
|
|
||||||
if (document.readyState !== "loading") {
|
|
||||||
func();
|
|
||||||
} else {
|
|
||||||
document.addEventListener("DOMContentLoaded", func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function gdMakeArticleActive(newId, noEvent) {
|
function gdMakeArticleActive(newId, noEvent) {
|
||||||
const gdCurrentArticle =
|
const gdCurrentArticle =
|
||||||
document.querySelector(".gdactivearticle").attributes.id;
|
document.querySelector(".gdactivearticle").attributes.id;
|
||||||
|
|
|
@ -4025,7 +4025,7 @@ void MainWindow::on_importFavorites_triggered()
|
||||||
QString fileName = QFileDialog::getOpenFileName( this,
|
QString fileName = QFileDialog::getOpenFileName( this,
|
||||||
tr( "Import Favorites from file" ),
|
tr( "Import Favorites from file" ),
|
||||||
importPath,
|
importPath,
|
||||||
tr( "XML files (*.xml);;Txt files (*.txt);;All files (*.*)" ) );
|
tr( "Text and XML files (*.txt *.xml);;All files (*.*)" ) );
|
||||||
if ( fileName.size() == 0 ) {
|
if ( fileName.size() == 0 ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ Additionally, multiple strategies of automatic grouping are provided:
|
||||||
|
|
||||||
## Auto groups by dictionary language
|
## Auto groups by dictionary language
|
||||||
|
|
||||||
For formats like DSL, which has embedded language from / to metadata, GoldenDict will use the dictionary's built-in metadata.
|
For formats like DSL, which has embedded language from / to metadata, GD will use the dictionary's built-in metadata.
|
||||||
|
|
||||||
For other formats, GoldenDict will try to extract languages from the dictionary's name or its file name by finding `{id}-{id}` pair. The `{id}` is 2 or 3 letters ISO 639 codes. For example, if a dictionary named `some name en-zh`, it will be automatically grouped into `en-zh`.
|
For other formats, GD will try finding the last `{id}-{id}` pair delimited by non-alphabets in dictionary name or main file name to extract languages. The `{id}` is 2 or 3 letters ISO 639 codes. For example, if a dictionary named `some name en-zh`, it will be automatically grouped into `en-zh`.
|
||||||
|
|
||||||
Groups created in this method also include a context menu when right-click the group name, in which you can do additional dictionaries grouping by source or target language and combine dictionaries in more large groups.
|
Groups created in this method also include a context menu when right-click the group name, in which you can do additional dictionaries grouping by source or target language and combine dictionaries in more large groups.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue