Compare commits

...

11 commits

Author SHA1 Message Date
xiaoyifang 95b57a1a3c
Merge 15b918eb6a into fa9ad2fdf7 2024-11-20 00:14:54 -08:00
shenleban tongying fa9ad2fdf7
clean: delete useless fopen style mode char* API from File::Index
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-20 03:56:23 +00:00
shenleban tongying 8f42e2e073
refactor: port StarDict -> Ifo away from File::Index 2024-11-19 22:39:36 -05:00
shenleban tongying c892083b00
fix: stardict format's description HTML display (.ifo file) 2024-11-20 02:30:17 +00:00
atauzki 2d6e2a85ee
feat: Windows, use Fusion only when dark mode applied
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-19 12:41:13 +00:00
shenleban tongying 1fa0771716
fix: local audio files without extension are not added to dictAudioMap
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-19 02:35:15 +00:00
shenleban tongying bb87c55b1a
fix: manually deploy icu4c dylibs for macOS
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-18 09:43:19 +00:00
shenleban tongying 652da8e1ec
action: update brew before running macOS workflows
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-18 03:07:37 +00:00
autofix-ci[bot] 15b918eb6a
[autofix.ci] apply automated fixes 2024-11-08 01:47:29 +00:00
xiaoyifang 27cbb7351b opt: add option about 2024-11-06 13:35:22 +08:00
xiaoyifang c787a08d2f opt: add option about 2024-11-06 12:07:23 +08:00
30 changed files with 169 additions and 179 deletions

View file

@ -51,6 +51,9 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: false submodules: false
- name: Update brew
run: |
brew update
- name: Install dependencies - name: Install dependencies
run: | run: |
brew install \ brew install \
@ -66,11 +69,11 @@ jobs:
xapian \ xapian \
libzim \ libzim \
qt qt
- name: Install eb
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
tar xvjf eb-4.4.3.tar.bz2 tar xvjf eb-4.4.3.tar.bz2
cd eb-4.4.3 && ./configure && make -j 8 && sudo make install && cd .. cd eb-4.4.3 && ./configure && make -j 8 && sudo make install && cd ..
- name: Run build - name: Run build
run: | run: |
mkdir build_dir mkdir build_dir

View file

@ -26,6 +26,9 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Update brew
run: |
brew update
- name: Install dependencies - name: Install dependencies
run: | run: |
brew install \ brew install \
@ -40,7 +43,8 @@ jobs:
ninja \ ninja \
opencc \ opencc \
xapian xapian
- name: Install eb
run: |
git clone https://github.com/xiaoyifang/eb.git git clone https://github.com/xiaoyifang/eb.git
cd eb && ./configure && make -j 8 && sudo make install && cd .. cd eb && ./configure && make -j 8 && sudo make install && cd ..
- uses: jurplel/install-qt-action@v4 - uses: jurplel/install-qt-action@v4
@ -61,6 +65,9 @@ jobs:
- name: Package - name: Package
run: | run: |
cmake --install build_dir/ cmake --install build_dir/
- name: Print package content
run: |
ls -Rl ./build_dir/redist
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: macOS-${{ matrix.os }}-Qt${{ matrix.qt_ver }} name: macOS-${{ matrix.os }}-Qt${{ matrix.qt_ver }}

View file

@ -284,6 +284,7 @@ if (APPLE)
set(QT_DEPLOY_TRANSLATIONS_DIR \"Contents/Resources/translations\") set(QT_DEPLOY_TRANSLATIONS_DIR \"Contents/Resources/translations\")
qt_deploy_runtime_dependencies( qt_deploy_runtime_dependencies(
EXECUTABLE \"${Redistributable_APP}\" EXECUTABLE \"${Redistributable_APP}\"
ADDITIONAL_LIBRARIES ${BREW_ICU_ADDITIONAL_DYLIBS}
GENERATE_QT_CONF GENERATE_QT_CONF
NO_APP_STORE_COMPLIANCE) NO_APP_STORE_COMPLIANCE)
qt_deploy_translations() qt_deploy_translations()

View file

@ -79,30 +79,25 @@ endif ()
if (WITH_ZIM) if (WITH_ZIM)
if (APPLE) if (APPLE)
# ICU from homebrew is "key-only", we need to manually prioritize it -> see `brew info icu4c` # ICU from homebrew is "key-only", we need to manually prioritize it -> see `brew info icu4c`
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/icu4c@76/lib/pkgconfig:/opt/homebrew/opt/icu4c@76/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig:/opt/homebrew/opt/icu4c/lib/pkgconfig") # And we needs to find the correct one if multiple versions co exists.
set(ENV{PATH} "$ENV{PATH}:/usr/local/bin/:/opt/homebrew/bin") # add brew command into PATH
execute_process(
COMMAND sh -c [=[brew --prefix $(brew deps libzim | grep icu4c)]=]
OUTPUT_VARIABLE ICU_REQUIRED_BY_ZIM_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
message(STATUS "Found correct homebrew icu path -> ${ICU_REQUIRED_BY_ZIM_PREFIX}")
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${ICU_REQUIRED_BY_ZIM_PREFIX}/lib/pkgconfig")
message(STATUS "Updated pkg_config_path -> $ENV{PKG_CONFIG_PATH}:${ICU_REQUIRED_BY_ZIM_PREFIX}/lib/pkgconfig")
# icu4c as transitive dependency of libzim may not be automatically copied into app bundle
# so we manually discover the icu4c from homebrew, then find the relevent dylibs
set(BREW_ICU_ADDITIONAL_DYLIBS "${ICU_REQUIRED_BY_ZIM_PREFIX}/lib/libicudata.dylib ${ICU_REQUIRED_BY_ZIM_PREFIX}/lib/libicui18n.dylib ${ICU_REQUIRED_BY_ZIM_PREFIX}/lib/libicuuc.dylib")
message(STATUS "Additional ICU `.dylib`s -> ${BREW_ICU_ADDITIONAL_DYLIBS}")
endif () endif ()
pkg_check_modules(ZIM REQUIRED IMPORTED_TARGET libzim) pkg_check_modules(ZIM REQUIRED IMPORTED_TARGET libzim)
target_link_libraries(${GOLDENDICT} PRIVATE PkgConfig::ZIM) target_link_libraries(${GOLDENDICT} PRIVATE PkgConfig::ZIM)
if (APPLE)
# icu4c as transitive dependency of libzim may not be copied into app bundle, so we directly depends on it to assist macdeployqt
# Why such complexities: 1) System or XCode SDKS's icu exists 2) icu itself is depended by various stuffs and homebrew may need multiple versions of it
pkg_check_modules(BREW_ICU REQUIRED IMPORTED_TARGET icu-i18n icu-uc)
target_link_libraries(${GOLDENDICT} PUBLIC PkgConfig::BREW_ICU)
# Verify icu <-> zim matches
message("Zim include dirs -> ${ZIM_INCLUDE_DIRS}")
message("Homebrew icu include dirs-> ${BREW_ICU_INCLUDE_DIRS}")
list(GET BREW_ICU_INCLUDE_DIRS 0 ONE_OF_BREW_ICU_INCLUDE_DIR)
if (ONE_OF_BREW_ICU_INCLUDE_DIR IN_LIST ZIM_INCLUDE_DIRS)
message("ZIM OK!")
else ()
message(FATAL_ERROR "!!!! ZIM <-> icu error -> check `brew info libzim` and `brew list libzim`")
endif ()
# TODO: get rid of these 💩, how?
endif ()
endif () endif ()
if (USE_SYSTEM_FMT) if (USE_SYSTEM_FMT)

View file

@ -257,9 +257,14 @@ inline bool isAudioUrl( QUrl const & url )
{ {
if ( !url.isValid() ) if ( !url.isValid() )
return false; return false;
// Note: we check for forvo sound links explicitly, as they don't have extensions
return ( url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "gdau" ) // gdau links are known to be audios, (sometimes they may not have file extension).
if ( url.scheme() == "gdau" ) {
return true;
}
// Note: we check for forvo sound links explicitly, as they don't have extensions
return ( url.scheme() == "http" || url.scheme() == "https" )
&& ( Filetype::isNameOfSound( url.path().toUtf8().data() ) || url.host() == "apifree.forvo.com" ); && ( Filetype::isNameOfSound( url.path().toUtf8().data() ) || url.host() == "apifree.forvo.com" );
} }

View file

@ -150,6 +150,7 @@ Preferences::Preferences():
doubleClickTranslates( true ), doubleClickTranslates( true ),
selectWordBySingleClick( false ), selectWordBySingleClick( false ),
autoScrollToTargetArticle( true ), autoScrollToTargetArticle( true ),
targetArticleAtFirst( false ),
escKeyHidesMainWindow( false ), escKeyHidesMainWindow( false ),
alwaysOnTop( false ), alwaysOnTop( false ),
searchInDock( false ), searchInDock( false ),
@ -878,6 +879,11 @@ Class load()
( preferences.namedItem( "autoScrollToTargetArticle" ).toElement().text() == "1" ); ( preferences.namedItem( "autoScrollToTargetArticle" ).toElement().text() == "1" );
} }
if ( !preferences.namedItem( "targetArticleAtFirst" ).isNull() ) {
c.preferences.targetArticleAtFirst =
( preferences.namedItem( "targetArticleAtFirst" ).toElement().text() == "1" );
}
if ( !preferences.namedItem( "escKeyHidesMainWindow" ).isNull() ) { if ( !preferences.namedItem( "escKeyHidesMainWindow" ).isNull() ) {
c.preferences.escKeyHidesMainWindow = c.preferences.escKeyHidesMainWindow =
( preferences.namedItem( "escKeyHidesMainWindow" ).toElement().text() == "1" ); ( preferences.namedItem( "escKeyHidesMainWindow" ).toElement().text() == "1" );
@ -1815,6 +1821,10 @@ void save( Class const & c )
opt.appendChild( dd.createTextNode( c.preferences.autoScrollToTargetArticle ? "1" : "0" ) ); opt.appendChild( dd.createTextNode( c.preferences.autoScrollToTargetArticle ? "1" : "0" ) );
preferences.appendChild( opt ); preferences.appendChild( opt );
opt = dd.createElement( "targetArticleAtFirst" );
opt.appendChild( dd.createTextNode( c.preferences.targetArticleAtFirst ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "escKeyHidesMainWindow" ); opt = dd.createElement( "escKeyHidesMainWindow" );
opt.appendChild( dd.createTextNode( c.preferences.escKeyHidesMainWindow ? "1" : "0" ) ); opt.appendChild( dd.createTextNode( c.preferences.escKeyHidesMainWindow ? "1" : "0" ) );
preferences.appendChild( opt ); preferences.appendChild( opt );

View file

@ -297,6 +297,7 @@ struct Preferences
bool doubleClickTranslates; bool doubleClickTranslates;
bool selectWordBySingleClick; bool selectWordBySingleClick;
bool autoScrollToTargetArticle; bool autoScrollToTargetArticle;
bool targetArticleAtFirst;
bool escKeyHidesMainWindow; bool escKeyHidesMainWindow;
bool alwaysOnTop; bool alwaysOnTop;

View file

@ -103,7 +103,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -287,10 +287,10 @@ private:
AardDictionary::AardDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): AardDictionary::AardDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
chunks( idx, idxHeader.chunksOffset ), chunks( idx, idxHeader.chunksOffset ),
df( dictionaryFiles[ 0 ], "rb" ) df( dictionaryFiles[ 0 ], QIODevice::ReadOnly )
{ {
// Read dictionary name // Read dictionary name
@ -802,7 +802,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
} }
} }
File::Index df( fileName, "rb" ); File::Index df( fileName, QIODevice::ReadOnly );
AAR_header dictHeader; AAR_header dictHeader;
@ -871,7 +871,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( dictName ); initializing.indexingDictionary( dictName );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
memset( &idxHeader, 0, sizeof( idxHeader ) ); memset( &idxHeader, 0, sizeof( idxHeader ) );

View file

@ -85,7 +85,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -250,7 +250,7 @@ private:
BglDictionary::BglDictionary( string const & id, string const & indexFile, string const & dictionaryFile ): BglDictionary::BglDictionary( string const & id, string const & indexFile, string const & dictionaryFile ):
BtreeDictionary( id, vector< string >( 1, dictionaryFile ) ), BtreeDictionary( id, vector< string >( 1, dictionaryFile ) ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
chunks( idx, idxHeader.chunksOffset ) chunks( idx, idxHeader.chunksOffset )
{ {
@ -1083,7 +1083,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( b.title() ); initializing.indexingDictionary( b.title() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;

View file

@ -74,7 +74,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -155,8 +155,8 @@ DictdDictionary::DictdDictionary( string const & id,
string const & indexFile, string const & indexFile,
vector< string > const & dictionaryFiles ): vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
indexFile( dictionaryFiles[ 0 ], "rb" ), indexFile( dictionaryFiles[ 0 ], QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ) idxHeader( idx.read< IdxHeader >() )
{ {
@ -606,7 +606,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( dictionaryName ); initializing.indexingDictionary( dictionaryName );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
@ -619,7 +619,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
IndexedWords indexedWords; IndexedWords indexedWords;
File::Index indexFile( dictFiles[ 0 ], "rb" ); File::Index indexFile( dictFiles[ 0 ], QIODevice::ReadOnly );
// Read words from index until none's left. // Read words from index until none's left.

View file

@ -132,7 +132,7 @@ struct InsidedCard
bool indexIsOldOrBad( string const & indexFile, bool hasZipFile ) bool indexIsOldOrBad( string const & indexFile, bool hasZipFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -289,7 +289,7 @@ private:
DslDictionary::DslDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): DslDictionary::DslDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
dz( 0 ), dz( 0 ),
deferredInitRunnableStarted( false ), deferredInitRunnableStarted( false ),
@ -1807,7 +1807,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
gdDebug( "Dsl: Building the index for dictionary: %s\n", gdDebug( "Dsl: Building the index for dictionary: %s\n",
QString::fromStdU32String( scanner.getDictionaryName() ).toUtf8().data() ); QString::fromStdU32String( scanner.getDictionaryName() ).toUtf8().data() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;

View file

@ -66,7 +66,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -219,7 +219,7 @@ EpwingDictionary::EpwingDictionary( string const & id,
vector< string > const & dictionaryFiles, vector< string > const & dictionaryFiles,
int subBook ): int subBook ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
chunks( idx, idxHeader.chunksOffset ) chunks( idx, idxHeader.chunksOffset )
{ {
@ -1197,7 +1197,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
QByteArray nameData = str.toUtf8(); QByteArray nameData = str.toUtf8();
initializing.indexingDictionary( nameData.data() ); initializing.indexingDictionary( nameData.data() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader{}; IdxHeader idxHeader{};

View file

@ -335,7 +335,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile, bool hasZipFile ) bool indexIsOldOrBad( string const & indexFile, bool hasZipFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -443,7 +443,7 @@ private:
GlsDictionary::GlsDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): GlsDictionary::GlsDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
dz( 0 ), dz( 0 ),
chunks( idx, idxHeader.chunksOffset ) chunks( idx, idxHeader.chunksOffset )
@ -1270,7 +1270,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
gdDebug( "Gls: Building the index for dictionary: %s\n", gdDebug( "Gls: Building the index for dictionary: %s\n",
QString::fromStdU32String( scanner.getDictionaryName() ).toUtf8().data() ); QString::fromStdU32String( scanner.getDictionaryName() ).toUtf8().data() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;

View file

@ -65,7 +65,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -201,7 +201,7 @@ string LsaDictionary::getName() noexcept
LsaDictionary::LsaDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): LsaDictionary::LsaDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ) idxHeader( idx.read< IdxHeader >() )
{ {
// Initialize the index // Initialize the index
@ -405,7 +405,7 @@ sptr< Dictionary::DataRequest > LsaDictionary::getResource( string const & name
return std::make_shared< Dictionary::DataRequestInstant >( false ); // No such resource return std::make_shared< Dictionary::DataRequestInstant >( false ); // No such resource
} }
File::Index f( getDictionaryFilenames()[ 0 ], "rb" ); File::Index f( getDictionaryFilenames()[ 0 ], QIODevice::ReadOnly );
f.seek( chain[ 0 ].articleOffset ); f.seek( chain[ 0 ].articleOffset );
Entry e( f ); Entry e( f );
@ -522,7 +522,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
} }
try { try {
File::Index f( *i, "rb" ); File::Index f( *i, QIODevice::ReadOnly );
/// Check the signature /// Check the signature
@ -547,7 +547,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( Utils::Fs::basename( *i ) ); initializing.indexingDictionary( Utils::Fs::basename( *i ) );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;

View file

@ -302,7 +302,7 @@ private:
MdxDictionary::MdxDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): MdxDictionary::MdxDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxFileName( indexFile ), idxFileName( indexFile ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
chunks( idx, idxHeader.chunksOffset ), chunks( idx, idxHeader.chunksOffset ),
@ -1307,7 +1307,7 @@ private:
static bool indexIsOldOrBad( vector< string > const & dictFiles, string const & indexFile ) static bool indexIsOldOrBad( vector< string > const & dictFiles, string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
return idx.readRecords( &header, sizeof( header ), 1 ) != 1 || header.signature != kSignature return idx.readRecords( &header, sizeof( header ), 1 ) != 1 || header.signature != kSignature
@ -1385,7 +1385,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
} }
} }
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
memset( &idxHeader, 0, sizeof( idxHeader ) ); memset( &idxHeader, 0, sizeof( idxHeader ) );
// We write a dummy header first. At the end of the process the header // We write a dummy header first. At the end of the process the header

View file

@ -97,7 +97,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -188,10 +188,10 @@ SdictDictionary::SdictDictionary( string const & id,
string const & indexFile, string const & indexFile,
vector< string > const & dictionaryFiles ): vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
chunks( idx, idxHeader.chunksOffset ), chunks( idx, idxHeader.chunksOffset ),
df( dictionaryFiles[ 0 ], "rb" ) df( dictionaryFiles[ 0 ], QIODevice::ReadOnly )
{ {
// Read dictionary name // Read dictionary name
@ -689,7 +689,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
try { try {
gdDebug( "SDict: Building the index for dictionary: %s\n", fileName.c_str() ); gdDebug( "SDict: Building the index for dictionary: %s\n", fileName.c_str() );
File::Index df( fileName, "rb" ); File::Index df( fileName, QIODevice::ReadOnly );
DCT_header dictHeader; DCT_header dictHeader;
@ -722,7 +722,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( dictName ); initializing.indexingDictionary( dictName );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
memset( &idxHeader, 0, sizeof( idxHeader ) ); memset( &idxHeader, 0, sizeof( idxHeader ) );

View file

@ -97,7 +97,7 @@ struct RefEntry
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -702,7 +702,7 @@ private:
SlobDictionary::SlobDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): SlobDictionary::SlobDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idxFileName( indexFile ), idxFileName( indexFile ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ) idxHeader( idx.read< IdxHeader >() )
{ {
// Open data file // Open data file
@ -1285,7 +1285,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( sf.getDictionaryName().toUtf8().constData() ); initializing.indexingDictionary( sf.getDictionaryName().toUtf8().constData() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
memset( &idxHeader, 0, sizeof( idxHeader ) ); memset( &idxHeader, 0, sizeof( idxHeader ) );

View file

@ -51,7 +51,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -114,7 +114,7 @@ SoundDirDictionary::SoundDirDictionary( string const & id,
QString const & iconFilename_ ): QString const & iconFilename_ ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
name( name_ ), name( name_ ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
chunks( idx, idxHeader.chunksOffset ), chunks( idx, idxHeader.chunksOffset ),
iconFilename( iconFilename_ ) iconFilename( iconFilename_ )
@ -370,7 +370,7 @@ sptr< Dictionary::DataRequest > SoundDirDictionary::getResource( string const &
// Now try loading that file // Now try loading that file
try { try {
File::Index f( fileName.toStdString(), "rb" ); File::Index f( fileName.toStdString(), QIODevice::ReadOnly );
sptr< Dictionary::DataRequestInstant > dr = std::make_shared< Dictionary::DataRequestInstant >( true ); sptr< Dictionary::DataRequestInstant > dr = std::make_shared< Dictionary::DataRequestInstant >( true );
@ -479,7 +479,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( Config::SoundDirs const &
initializing.indexingDictionary( soundDir.name.toUtf8().data() ); initializing.indexingDictionary( soundDir.name.toUtf8().data() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;

View file

@ -76,13 +76,15 @@ DEF_EX_STR( exIncorrectOffset, "Incorrect offset encountered in file", Dictionar
/// Contents of an ifo file /// Contents of an ifo file
struct Ifo struct Ifo
{ {
string version;
string bookname; string bookname;
uint32_t wordcount, synwordcount, idxfilesize, idxoffsetbits; uint32_t wordcount = 0;
uint32_t synwordcount = 0;
uint32_t idxfilesize = 0;
uint32_t idxoffsetbits = 32;
string sametypesequence, dicttype, description; string sametypesequence, dicttype, description;
string copyright, author, email, website, date; string copyright, author, email, website, date;
explicit Ifo( File::Index & ); explicit Ifo( const QString & fileName );
}; };
enum { enum {
@ -116,7 +118,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -235,7 +237,7 @@ StardictDictionary::StardictDictionary( string const & id,
string const & indexFile, string const & indexFile,
vector< string > const & dictionaryFiles ): vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
bookName( loadString( idxHeader.bookNameSize ) ), bookName( loadString( idxHeader.bookNameSize ) ),
sameTypeSequence( loadString( idxHeader.sameTypeSequenceSize ) ), sameTypeSequence( loadString( idxHeader.sameTypeSequenceSize ) ),
@ -1085,40 +1087,36 @@ QString const & StardictDictionary::getDescription()
return dictionaryDescription; return dictionaryDescription;
} }
File::Index ifoFile( getDictionaryFilenames()[ 0 ], "r" ); Ifo ifo( QString::fromStdString( getDictionaryFilenames()[ 0 ] ) );
Ifo ifo( ifoFile );
if ( !ifo.copyright.empty() ) { if ( !ifo.copyright.empty() ) {
QString copyright = QString::fromUtf8( ifo.copyright.c_str() ).replace( "<br>", "\n", Qt::CaseInsensitive ); QString copyright = QString::fromUtf8( ifo.copyright.c_str() );
dictionaryDescription += QObject::tr( "Copyright: %1%2" ).arg( copyright ).arg( "\n\n" ); dictionaryDescription += QObject::tr( "Copyright: %1%2" ).arg( copyright ).arg( "<br><br>" );
} }
if ( !ifo.author.empty() ) { if ( !ifo.author.empty() ) {
QString author = QString::fromUtf8( ifo.author.c_str() ); QString author = QString::fromUtf8( ifo.author.c_str() );
dictionaryDescription += QObject::tr( "Author: %1%2" ).arg( author ).arg( "\n\n" ); dictionaryDescription += QObject::tr( "Author: %1%2" ).arg( author ).arg( "<br><br>" );
} }
if ( !ifo.email.empty() ) { if ( !ifo.email.empty() ) {
QString email = QString::fromUtf8( ifo.email.c_str() ); QString email = QString::fromUtf8( ifo.email.c_str() );
dictionaryDescription += QObject::tr( "E-mail: %1%2" ).arg( email ).arg( "\n\n" ); dictionaryDescription += QObject::tr( "E-mail: %1%2" ).arg( email ).arg( "<br><br>" );
} }
if ( !ifo.website.empty() ) { if ( !ifo.website.empty() ) {
QString website = QString::fromUtf8( ifo.website.c_str() ); QString website = QString::fromUtf8( ifo.website.c_str() );
dictionaryDescription += QObject::tr( "Website: %1%2" ).arg( website ).arg( "\n\n" ); dictionaryDescription += QObject::tr( "Website: %1%2" ).arg( website ).arg( "<br><br>" );
} }
if ( !ifo.date.empty() ) { if ( !ifo.date.empty() ) {
QString date = QString::fromUtf8( ifo.date.c_str() ); QString date = QString::fromUtf8( ifo.date.c_str() );
dictionaryDescription += QObject::tr( "Date: %1%2" ).arg( date ).arg( "\n\n" ); dictionaryDescription += QObject::tr( "Date: %1%2" ).arg( date ).arg( "<br><br>" );
} }
if ( !ifo.description.empty() ) { if ( !ifo.description.empty() ) {
QString desc = QString::fromUtf8( ifo.description.c_str() ); QString desc = QString::fromUtf8( ifo.description.c_str() );
desc.replace( "\t", "<br/>" ); dictionaryDescription += desc;
desc.replace( "\\n", "<br/>" );
desc.replace( "<br>", "<br/>", Qt::CaseInsensitive );
dictionaryDescription += Html::unescape( desc, Html::HtmlOption::Keep );
} }
if ( dictionaryDescription.isEmpty() ) { if ( dictionaryDescription.isEmpty() ) {
@ -1457,85 +1455,77 @@ static char const * beginsWith( char const * substr, char const * str )
return strncmp( str, substr, len ) == 0 ? str + len : 0; return strncmp( str, substr, len ) == 0 ? str + len : 0;
} }
Ifo::Ifo( File::Index & f ): Ifo::Ifo( const QString & fileName )
wordcount( 0 ),
synwordcount( 0 ),
idxfilesize( 0 ),
idxoffsetbits( 32 )
{ {
static string const versionEq( "version=" ); QFile f( fileName );
if ( !f.open( QIODevice::ReadOnly ) ) {
throw exCantReadFile( "Cannot open IFO file -> " + fileName.toStdString() );
};
static string const booknameEq( "bookname=" ); if ( !f.readLine().startsWith( "StarDict's dict ifo file" ) || !f.readLine().startsWith( "version=" ) ) {
//GD_DPRINTF( "%s<\n", f.gets().c_str() );
//GD_DPRINTF( "%s<\n", f.gets().c_str() );
if ( QString::fromUtf8( f.gets().c_str() ) != "StarDict's dict ifo file"
|| f.gets().compare( 0, versionEq.size(), versionEq ) ) {
throw exNotAnIfoFile(); throw exNotAnIfoFile();
} }
/// Now go through the file and parse options /// Now go through the file and parse options
{
while ( !f.atEnd() ) {
auto line = f.readLine();
auto option = QByteArrayView( line ).trimmed();
// Empty lines are allowed in .ifo file
try { if ( option.isEmpty() ) {
char option[ 16384 ]; continue;
for ( ;; ) {
if ( !f.gets( option, sizeof( option ), true ) ) {
break;
} }
if ( char const * val = beginsWith( "bookname=", option ) ) { if ( char const * val = beginsWith( "bookname=", option.data() ) ) {
bookname = val; bookname = val;
} }
else if ( char const * val = beginsWith( "wordcount=", option ) ) { else if ( char const * val = beginsWith( "wordcount=", option.data() ) ) {
if ( sscanf( val, "%u", &wordcount ) != 1 ) { if ( sscanf( val, "%u", &wordcount ) != 1 ) {
throw exBadFieldInIfo( option ); throw exBadFieldInIfo( option.data() );
} }
} }
else if ( char const * val = beginsWith( "synwordcount=", option ) ) { else if ( char const * val = beginsWith( "synwordcount=", option.data() ) ) {
if ( sscanf( val, "%u", &synwordcount ) != 1 ) { if ( sscanf( val, "%u", &synwordcount ) != 1 ) {
throw exBadFieldInIfo( option ); throw exBadFieldInIfo( option.data() );
} }
} }
else if ( char const * val = beginsWith( "idxfilesize=", option ) ) { else if ( char const * val = beginsWith( "idxfilesize=", option.data() ) ) {
if ( sscanf( val, "%u", &idxfilesize ) != 1 ) { if ( sscanf( val, "%u", &idxfilesize ) != 1 ) {
throw exBadFieldInIfo( option ); throw exBadFieldInIfo( option.data() );
} }
} }
else if ( char const * val = beginsWith( "idxoffsetbits=", option ) ) { else if ( char const * val = beginsWith( "idxoffsetbits=", option.data() ) ) {
if ( sscanf( val, "%u", &idxoffsetbits ) != 1 || ( idxoffsetbits != 32 && idxoffsetbits != 64 ) ) { if ( sscanf( val, "%u", &idxoffsetbits ) != 1 || ( idxoffsetbits != 32 && idxoffsetbits != 64 ) ) {
throw exBadFieldInIfo( option ); throw exBadFieldInIfo( option.data() );
} }
} }
else if ( char const * val = beginsWith( "sametypesequence=", option ) ) { else if ( char const * val = beginsWith( "sametypesequence=", option.data() ) ) {
sametypesequence = val; sametypesequence = val;
} }
else if ( char const * val = beginsWith( "dicttype=", option ) ) { else if ( char const * val = beginsWith( "dicttype=", option.data() ) ) {
dicttype = val; dicttype = val;
} }
else if ( char const * val = beginsWith( "description=", option ) ) { else if ( char const * val = beginsWith( "description=", option.data() ) ) {
description = val; description = val;
} }
else if ( char const * val = beginsWith( "copyright=", option ) ) { else if ( char const * val = beginsWith( "copyright=", option.data() ) ) {
copyright = val; copyright = val;
} }
else if ( char const * val = beginsWith( "author=", option ) ) { else if ( char const * val = beginsWith( "author=", option.data() ) ) {
author = val; author = val;
} }
else if ( char const * val = beginsWith( "email=", option ) ) { else if ( char const * val = beginsWith( "email=", option.data() ) ) {
email = val; email = val;
} }
else if ( char const * val = beginsWith( "website=", option ) ) { else if ( char const * val = beginsWith( "website=", option.data() ) ) {
website = val; website = val;
} }
else if ( char const * val = beginsWith( "date=", option ) ) { else if ( char const * val = beginsWith( "date=", option.data() ) ) {
date = val; date = val;
} }
} }
} }
catch ( File::exReadError & ) {
}
} }
//// StardictDictionary::getResource() //// StardictDictionary::getResource()
@ -1897,9 +1887,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
if ( Dictionary::needToRebuildIndex( dictFiles, indexFile ) || indexIsOldOrBad( indexFile ) ) { if ( Dictionary::needToRebuildIndex( dictFiles, indexFile ) || indexIsOldOrBad( indexFile ) ) {
// Building the index // Building the index
File::Index ifoFile( fileName, "r" ); Ifo ifo( QString::fromStdString( fileName ) );
Ifo ifo( ifoFile );
gdDebug( "Stardict: Building the index for dictionary: %s\n", ifo.bookname.c_str() ); gdDebug( "Stardict: Building the index for dictionary: %s\n", ifo.bookname.c_str() );
@ -1930,7 +1918,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( ifo.bookname ); initializing.indexingDictionary( ifo.bookname );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;

View file

@ -37,50 +37,18 @@ bool tryPossibleZipName( std::string const & name, std::string & copyTo )
void loadFromFile( std::string const & filename, std::vector< char > & data ) void loadFromFile( std::string const & filename, std::vector< char > & data )
{ {
File::Index f( filename, "rb" ); File::Index f( filename, QIODevice::ReadOnly );
auto size = f.file().size(); // QFile::size() obtains size via statx on Linux auto size = f.file().size(); // QFile::size() obtains size via statx on Linux
data.resize( size ); data.resize( size );
f.read( data.data(), size ); f.read( data.data(), size );
} }
void Index::open( char const * mode ) Index::Index( std::string_view filename, QIODevice::OpenMode mode )
{
QFile::OpenMode openMode = QIODevice::Text;
const char * pch = mode;
while ( *pch ) {
switch ( *pch ) {
case 'r':
openMode |= QIODevice::ReadOnly;
break;
case 'w':
openMode |= QIODevice::WriteOnly;
break;
case '+':
openMode &= ~( QIODevice::ReadOnly | QIODevice::WriteOnly );
openMode |= QIODevice::ReadWrite;
break;
case 'a':
openMode |= QIODevice::Append;
break;
case 'b':
openMode &= ~QIODevice::Text;
break;
default:
break;
}
++pch;
}
if ( !f.open( openMode ) ) {
throw exCantOpen( f.fileName().toStdString() + ": " + f.errorString().toUtf8().data() );
}
}
Index::Index( std::string_view filename, char const * mode )
{ {
f.setFileName( QString::fromUtf8( filename.data(), filename.size() ) ); f.setFileName( QString::fromUtf8( filename.data(), filename.size() ) );
open( mode ); if ( !f.open( mode ) ) {
throw exCantOpen( ( f.fileName() + ": " + f.errorString() ).toStdString() );
}
} }
void Index::read( void * buf, qint64 size ) void Index::read( void * buf, qint64 size )

View file

@ -43,7 +43,7 @@ public:
QMutex lock; QMutex lock;
// Create QFile Object and open() it. // Create QFile Object and open() it.
Index( std::string_view filename, char const * mode ); Index( std::string_view filename, QIODevice::OpenMode mode );
/// QFile::read & QFile::write , but with exception throwing /// QFile::read & QFile::write , but with exception throwing
void read( void * buf, qint64 size ); void read( void * buf, qint64 size );
@ -113,8 +113,6 @@ public:
~Index() noexcept; ~Index() noexcept;
private: private:
// QFile::open but with fopen-like mode settings.
void open( char const * mode );
template< typename T > template< typename T >
void readType( T & value ) void readType( T & value )

View file

@ -125,7 +125,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -228,7 +228,7 @@ private:
XdxfDictionary::XdxfDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): XdxfDictionary::XdxfDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ) idxHeader( idx.read< IdxHeader >() )
{ {
// Read the dictionary name // Read the dictionary name
@ -1077,7 +1077,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
//initializing.indexingDictionary( nameFromFileName( dictFiles[ 0 ] ) ); //initializing.indexingDictionary( nameFromFileName( dictFiles[ 0 ] ) );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
map< string, string > abrv; map< string, string > abrv;

View file

@ -93,7 +93,7 @@ static_assert( alignof( IdxHeader ) == 1 );
// Some supporting functions // Some supporting functions
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -241,7 +241,7 @@ private:
ZimDictionary::ZimDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ): ZimDictionary::ZimDictionary( string const & id, string const & indexFile, vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ), idxHeader( idx.read< IdxHeader >() ),
df( dictionaryFiles[ 0 ] ) df( dictionaryFiles[ 0 ] )
{ {
@ -832,7 +832,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
initializing.indexingDictionary( firstName.mid( n + 1 ).toUtf8().constData() ); initializing.indexingDictionary( firstName.mid( n + 1 ).toUtf8().constData() );
} }
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
memset( &idxHeader, 0, sizeof( idxHeader ) ); memset( &idxHeader, 0, sizeof( idxHeader ) );
idxHeader.namePtr = 0xFFFFFFFF; idxHeader.namePtr = 0xFFFFFFFF;

View file

@ -60,7 +60,7 @@ static_assert( alignof( IdxHeader ) == 1 );
bool indexIsOldOrBad( string const & indexFile ) bool indexIsOldOrBad( string const & indexFile )
{ {
File::Index idx( indexFile, "rb" ); File::Index idx( indexFile, QIODevice::ReadOnly );
IdxHeader header; IdxHeader header;
@ -140,7 +140,7 @@ ZipSoundsDictionary::ZipSoundsDictionary( string const & id,
string const & indexFile, string const & indexFile,
vector< string > const & dictionaryFiles ): vector< string > const & dictionaryFiles ):
BtreeDictionary( id, dictionaryFiles ), BtreeDictionary( id, dictionaryFiles ),
idx( indexFile, "rb" ), idx( indexFile, QIODevice::ReadOnly ),
idxHeader( idx.read< IdxHeader >() ) idxHeader( idx.read< IdxHeader >() )
{ {
chunks = std::shared_ptr< ChunkedStorage::Reader >( new ChunkedStorage::Reader( idx, idxHeader.chunksOffset ) ); chunks = std::shared_ptr< ChunkedStorage::Reader >( new ChunkedStorage::Reader( idx, idxHeader.chunksOffset ) );
@ -405,7 +405,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
if ( Dictionary::needToRebuildIndex( dictFiles, indexFile ) || indexIsOldOrBad( indexFile ) ) { if ( Dictionary::needToRebuildIndex( dictFiles, indexFile ) || indexIsOldOrBad( indexFile ) ) {
gdDebug( "Zips: Building the index for dictionary: %s\n", fileName.c_str() ); gdDebug( "Zips: Building the index for dictionary: %s\n", fileName.c_str() );
File::Index idx( indexFile, "wb" ); File::Index idx( indexFile, QIODevice::WriteOnly );
IdxHeader idxHeader; IdxHeader idxHeader;
memset( &idxHeader, 0, sizeof( idxHeader ) ); memset( &idxHeader, 0, sizeof( idxHeader ) );

View file

@ -368,7 +368,7 @@ int main( int argc, char ** argv )
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// TODO: Force fusion because Qt6.7's "ModernStyle"'s dark theme have problems, need to test / reconsider in future // TODO: Force fusion because Qt6.7's "ModernStyle"'s dark theme have problems, need to test / reconsider in future
QHotkeyApplication::setStyle( QStyleFactory::create( "Fusion" ) ); QHotkeyApplication::setStyle( QStyleFactory::create( "WindowsVista" ) );
#endif #endif

View file

@ -17,7 +17,7 @@ void PronounceEngine::reset()
} }
void PronounceEngine::sendAudio( std::string dictId, QString audioLink ) void PronounceEngine::sendAudio( const std::string & dictId, const QString & audioLink )
{ {
if ( state == PronounceState::OCCUPIED ) { if ( state == PronounceState::OCCUPIED ) {
return; return;
@ -29,7 +29,7 @@ void PronounceEngine::sendAudio( std::string dictId, QString audioLink )
QMutexLocker _( &mutex ); QMutexLocker _( &mutex );
dictAudioMap.operator[]( dictId ).push_back( audioLink ); dictAudioMap[ dictId ].append( audioLink );
} }
void PronounceEngine::finishDictionary( std::string dictId ) void PronounceEngine::finishDictionary( std::string dictId )

View file

@ -21,7 +21,7 @@ class PronounceEngine: public QObject
public: public:
explicit PronounceEngine( QObject * parent = nullptr ); explicit PronounceEngine( QObject * parent = nullptr );
void reset(); void reset();
void sendAudio( std::string dictId, QString audioLink ); void sendAudio( const std::string & dictId, const QString & audioLink );
void finishDictionary( std::string dictId ); void finishDictionary( std::string dictId );
signals: signals:
void emitAudio( QString audioLink ); void emitAudio( QString audioLink );

View file

@ -1356,8 +1356,10 @@ void MainWindow::updateAppearances( QString const & addonStyle,
darkPalette.setColor( QPalette::Disabled, QPalette::HighlightedText, disabledColor ); darkPalette.setColor( QPalette::Disabled, QPalette::HighlightedText, disabledColor );
qApp->setPalette( darkPalette ); qApp->setPalette( darkPalette );
qApp->setStyle( "Fusion" );
} }
else { else {
qApp->setStyle( "WindowsVista" );
qApp->setPalette( QPalette() ); qApp->setPalette( QPalette() );
} }
#endif #endif

View file

@ -185,6 +185,7 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
ui.doubleClickTranslates->setChecked( p.doubleClickTranslates ); ui.doubleClickTranslates->setChecked( p.doubleClickTranslates );
ui.selectBySingleClick->setChecked( p.selectWordBySingleClick ); ui.selectBySingleClick->setChecked( p.selectWordBySingleClick );
ui.autoScrollToTargetArticle->setChecked( p.autoScrollToTargetArticle ); ui.autoScrollToTargetArticle->setChecked( p.autoScrollToTargetArticle );
ui.targetArticleAtFirst->setChecked( p.targetArticleAtFirst );
ui.escKeyHidesMainWindow->setChecked( p.escKeyHidesMainWindow ); ui.escKeyHidesMainWindow->setChecked( p.escKeyHidesMainWindow );
ui.darkMode->addItem( tr( "On" ), QVariant::fromValue( Config::Dark::On ) ); ui.darkMode->addItem( tr( "On" ), QVariant::fromValue( Config::Dark::On ) );
@ -441,6 +442,7 @@ Config::Preferences Preferences::getPreferences()
p.doubleClickTranslates = ui.doubleClickTranslates->isChecked(); p.doubleClickTranslates = ui.doubleClickTranslates->isChecked();
p.selectWordBySingleClick = ui.selectBySingleClick->isChecked(); p.selectWordBySingleClick = ui.selectBySingleClick->isChecked();
p.autoScrollToTargetArticle = ui.autoScrollToTargetArticle->isChecked(); p.autoScrollToTargetArticle = ui.autoScrollToTargetArticle->isChecked();
p.targetArticleAtFirst = ui.targetArticleAtFirst->isChecked();
p.escKeyHidesMainWindow = ui.escKeyHidesMainWindow->isChecked(); p.escKeyHidesMainWindow = ui.escKeyHidesMainWindow->isChecked();
p.darkMode = ui.darkMode->currentData().value< Config::Dark >(); p.darkMode = ui.darkMode->currentData().value< Config::Dark >();

View file

@ -169,6 +169,16 @@ however, the article from the topmost dictionary is shown.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QCheckBox" name="targetArticleAtFirst">
<property name="text">
<string>Place the target article at the first place.</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QGroupBox" name="enableTrayIcon"> <widget class="QGroupBox" name="enableTrayIcon">
<property name="toolTip"> <property name="toolTip">