Compare commits

...

15 commits

Author SHA1 Message Date
xiaoyifang b0de4e1d5d
Merge 15b918eb6a into c7d0feb2e8 2024-11-15 16:18:07 -05:00
shenleban tongying c7d0feb2e8 fix: macOS libzim <-> icu4c again and make mismatch a fatal error
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-15 15:41:44 -05:00
shenleban tongying c5d682c993
clean: fix some issues found by the code analysis of Visual Studio
Some checks failed
SonarCloud / Build and analyze (push) Has been cancelled
2024-11-14 12:24:46 +00:00
shenleban tongying 0f71f32ad4
refactor: move most of the Windows specific code out of hotkeywrapper.cc 2024-11-14 11:12:17 +00:00
shenleban tongying 5cc43a3dc8
fix: windows global hotkey Win/Meta and 1-9 doesn't work 2024-11-14 09:06:23 +00:00
shenleban tongying beef6dd138 clean: delete most Qt5 & <=6.3 conditional compiled code (QT_VERSION)
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-14 00:07:15 -05:00
shenleban tongying ccf70cdfb6 doc: various updates
Some checks failed
SonarCloud / Build and analyze (push) Waiting to run
deploy_website / deploy (push) Has been cancelled
2024-11-13 23:05:40 -05:00
shenleban tongying 6865b02547 fix: a DICT crash caused by string out of bound access
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-13 17:18:04 -05:00
shenleban tongying fbec70e41a opt: simplify Folding::apply
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-12 23:47:20 -05:00
shenleban tongying 20fcea33e1
Merge pull request #1946 from shenlebantongying/clean/rm-gd-toWstring
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
clean: remove redundant gd::toWString
2024-11-12 19:57:36 -05:00
shenleban tongying 9db5867ce8 dev: ignore last commit 2024-11-12 19:49:44 -05:00
shenleban tongying c8af0450f1 clean: remove redundant gd::toWString 2024-11-12 19:32:13 -05: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
58 changed files with 522 additions and 703 deletions

View file

@ -15,3 +15,6 @@ a11c9e3aeca4329e1982d8fe26bacbb21ab50ddf
# mass apply clang-tidy's modernize-use-using # mass apply clang-tidy's modernize-use-using
d15081e723756eef053550dc9e06e31d7828dec3 d15081e723756eef053550dc9e06e31d7828dec3
# remove gd::toWString
c8af0450f1f7f8188004db96e3f53e7e33e2ccad

View file

@ -3,11 +3,6 @@
if (APPLE) if (APPLE)
# old & new homebrew's include paths # old & new homebrew's include paths
target_include_directories(${GOLDENDICT} PRIVATE /usr/local/include /opt/homebrew/include) target_include_directories(${GOLDENDICT} PRIVATE /usr/local/include /opt/homebrew/include)
# libzim depends on ICU, but the ICU from homebrew is "key-only", we need to manually prioritize it
# See `brew info icu4c` if this no longer works
# Note: Remove icu4c@75 if it fails again
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/icu4c@75/lib/pkgconfig:/opt/homebrew/opt/icu4c@75/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig:/opt/homebrew/opt/icu4c/lib/pkgconfig")
endif () endif ()
target_include_directories(${GOLDENDICT} PRIVATE target_include_directories(${GOLDENDICT} PRIVATE
@ -82,13 +77,31 @@ if (WITH_EPWING_SUPPORT)
endif () endif ()
if (WITH_ZIM) if (WITH_ZIM)
if (APPLE)
# 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")
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) if (APPLE)
# For some reason, icu4c as transitive dependency of libzim may not be copied into app bundle, # icu4c as transitive dependency of libzim may not be copied into app bundle, so we directly depends on it to assist macdeployqt
# so we directly depends on it to help macdeployqt or whatever # 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_FOR_LIBZIM_FORCE_LINK REQUIRED IMPORTED_TARGET icu-i18n icu-uc) pkg_check_modules(BREW_ICU REQUIRED IMPORTED_TARGET icu-i18n icu-uc)
target_link_libraries(${GOLDENDICT} PUBLIC PkgConfig::BREW_ICU_FOR_LIBZIM_FORCE_LINK) 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 () endif ()

View file

@ -521,7 +521,7 @@ void ArticleRequest::altSearchFinished()
vector< wstring > altsVector( alts.begin(), alts.end() ); vector< wstring > altsVector( alts.begin(), alts.end() );
wstring wordStd = gd::toWString( word ); wstring wordStd = word.toStdU32String();
if ( activeDicts.size() <= 1 ) { if ( activeDicts.size() <= 1 ) {
articleSizeLimit = -1; // Don't collapse article if only one dictionary presented articleSizeLimit = -1; // Don't collapse article if only one dictionary presented

View file

@ -6,11 +6,7 @@
#include <QWaitCondition> #include <QWaitCondition>
#include <QCoreApplication> #include <QCoreApplication>
#include <QThreadPool> #include <QThreadPool>
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) #include <QAudioSink>
#include <QAudioOutput>
#else
#include <QAudioSink>
#endif
#include <QtGlobal> #include <QtGlobal>
#include <QBuffer> #include <QBuffer>
@ -23,18 +19,7 @@ static QAudioFormat format( int sampleRate, int channelCount )
out.setSampleRate( sampleRate ); out.setSampleRate( sampleRate );
out.setChannelCount( channelCount ); out.setChannelCount( channelCount );
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
out.setByteOrder( QAudioFormat::LittleEndian );
out.setCodec( QLatin1String( "audio/pcm" ) );
#endif
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
out.setSampleSize( 16 );
out.setSampleType( QAudioFormat::SignedInt );
#else
out.setSampleFormat( QAudioFormat::Int16 ); out.setSampleFormat( QAudioFormat::Int16 );
#endif
return out; return out;
} }
@ -50,11 +35,8 @@ public:
QFuture< void > audioPlayFuture; QFuture< void > audioPlayFuture;
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
using AudioOutput = QAudioOutput;
#else
using AudioOutput = QAudioSink; using AudioOutput = QAudioSink;
#endif
AudioOutput * audioOutput = nullptr; AudioOutput * audioOutput = nullptr;
QByteArray buffer; QByteArray buffer;
qint64 offset = 0; qint64 offset = 0;
@ -185,11 +167,7 @@ AudioOutput::AudioOutput( QObject * parent ):
QObject( parent ), QObject( parent ),
d_ptr( new AudioOutputPrivate ) d_ptr( new AudioOutputPrivate )
{ {
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
d_ptr->audioPlayFuture = QtConcurrent::run( &d_ptr->threadPool, d_ptr.data(), &AudioOutputPrivate::doPlayAudio );
#else
d_ptr->audioPlayFuture = QtConcurrent::run( &d_ptr->threadPool, &AudioOutputPrivate::doPlayAudio, d_ptr.data() ); d_ptr->audioPlayFuture = QtConcurrent::run( &d_ptr->threadPool, &AudioOutputPrivate::doPlayAudio, d_ptr.data() );
#endif
} }
void AudioOutput::setAudioFormat( int sampleRate, int channels ) void AudioOutput::setAudioFormat( int sampleRate, int channels )

View file

@ -2,27 +2,14 @@
#include "audiooutput.hh" #include "audiooutput.hh"
#include "ffmpegaudio.hh" #include "ffmpegaudio.hh"
#include <errno.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include "libswresample/swresample.h"
}
#include <QString>
#include <QDataStream>
#include <vector>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) )
#include <QMediaDevices>
#include <QAudioDevice>
#endif
#include "gddebug.hh" #include "gddebug.hh"
#include "utils.hh" #include "utils.hh"
#include <QAudioDevice>
#include <QDataStream>
#include <QMediaDevices>
#include <QString>
#include <errno.h>
#include <vector>
using std::vector; using std::vector;
@ -284,14 +271,11 @@ void DecoderContext::closeCodec()
bool DecoderContext::openOutputDevice( QString & errorString ) bool DecoderContext::openOutputDevice( QString & errorString )
{ {
// only check device when qt version is greater than 6.2
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) )
QAudioDevice m_outputDevice = QMediaDevices::defaultAudioOutput(); QAudioDevice m_outputDevice = QMediaDevices::defaultAudioOutput();
if ( m_outputDevice.isNull() ) { if ( m_outputDevice.isNull() ) {
errorString += QString( "Can not found default audio output device" ); errorString += QString( "Can not found default audio output device" );
return false; return false;
} }
#endif
if ( audioOutput == nullptr ) { if ( audioOutput == nullptr ) {
errorString += QStringLiteral( "Failed to create audioOutput." ); errorString += QStringLiteral( "Failed to create audioOutput." );

View file

@ -1,27 +1,24 @@
#pragma once #pragma once
#ifdef MAKE_FFMPEG_PLAYER #ifdef MAKE_FFMPEG_PLAYER
#include "audiooutput.hh"
#include <QObject>
#include <QMutex>
#include <QByteArray>
#include <QThread>
extern "C" { extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/avutil.h> #include <libavutil/avutil.h>
#include "libswresample/swresample.h" #include "libswresample/swresample.h"
} }
#include "audiooutput.hh"
#include <QString> #include <QObject>
#include <QMutex>
#include <QByteArray>
#include <QThread>
#include <QAudioDevice>
#include <QDataStream> #include <QDataStream>
#include <QMediaDevices>
#include <QString>
#include <vector> #include <vector>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) )
#include <QMediaDevices>
#include <QAudioDevice>
#endif
using std::vector; using std::vector;
namespace Ffmpeg { namespace Ffmpeg {
class DecoderThread; class DecoderThread;

View file

@ -4,29 +4,18 @@
#ifdef MAKE_QTMULTIMEDIA_PLAYER #ifdef MAKE_QTMULTIMEDIA_PLAYER
#include <QByteArray> #include <QByteArray>
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QAudioDevice>
#include <QMediaContent>
#endif
#if ( QT_VERSION > QT_VERSION_CHECK( 6, 2, 0 ) )
#include <QAudioDevice>
#endif
#include "multimediaaudioplayer.hh" #include "multimediaaudioplayer.hh"
#include <QDebug> #include <QDebug>
MultimediaAudioPlayer::MultimediaAudioPlayer() MultimediaAudioPlayer::MultimediaAudioPlayer()
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
:
player( 0, QMediaPlayer::StreamPlayback )
#endif
{ {
player.setAudioOutput( &audioOutput ); player.setAudioOutput( &audioOutput );
connect( &player, &QMediaPlayer::errorChanged, this, &MultimediaAudioPlayer::onMediaPlayerError ); connect( &player, &QMediaPlayer::errorChanged, this, &MultimediaAudioPlayer::onMediaPlayerError );
#if ( QT_VERSION > QT_VERSION_CHECK( 6, 2, 0 ) )
connect( &mediaDevices, &QMediaDevices::audioOutputsChanged, this, &MultimediaAudioPlayer::audioOutputChange ); connect( &mediaDevices, &QMediaDevices::audioOutputsChanged, this, &MultimediaAudioPlayer::audioOutputChange );
#endif
} }
void MultimediaAudioPlayer::audioOutputChange() void MultimediaAudioPlayer::audioOutputChange()
@ -42,15 +31,11 @@ QString MultimediaAudioPlayer::play( const char * data, int size )
if ( !audioBuffer->open( QIODevice::ReadOnly ) ) { if ( !audioBuffer->open( QIODevice::ReadOnly ) ) {
return tr( "Couldn't open audio buffer for reading." ); return tr( "Couldn't open audio buffer for reading." );
} }
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
player.setSourceDevice( audioBuffer ); player.setSourceDevice( audioBuffer );
#if ( QT_VERSION > QT_VERSION_CHECK( 6, 2, 0 ) )
audioOutput.setDevice( QMediaDevices::defaultAudioOutput() ); audioOutput.setDevice( QMediaDevices::defaultAudioOutput() );
player.setAudioOutput( &audioOutput ); player.setAudioOutput( &audioOutput );
#endif
#else
player.setMedia( QMediaContent(), audioBuffer );
#endif
player.play(); player.play();
return {}; return {};
} }
@ -58,9 +43,7 @@ QString MultimediaAudioPlayer::play( const char * data, int size )
void MultimediaAudioPlayer::stop() void MultimediaAudioPlayer::stop()
{ {
player.stop(); player.stop();
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
player.setMedia( QMediaContent() ); // Forget about audioBuffer.
#endif
if ( audioBuffer ) { if ( audioBuffer ) {
audioBuffer->close(); audioBuffer->close();
audioBuffer->setData( QByteArray() ); // Free memory. audioBuffer->setData( QByteArray() ); // Free memory.

View file

@ -5,15 +5,11 @@
#ifdef MAKE_QTMULTIMEDIA_PLAYER #ifdef MAKE_QTMULTIMEDIA_PLAYER
#include <QBuffer>
#include <QMediaPlayer>
#include "audioplayerinterface.hh" #include "audioplayerinterface.hh"
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QAudioOutput>
#include <QAudioOutput> #include <QBuffer>
#endif #include <QMediaDevices>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) ) #include <QMediaPlayer>
#include <QMediaDevices>
#endif
#include <QPointer> #include <QPointer>
class MultimediaAudioPlayer: public AudioPlayerInterface class MultimediaAudioPlayer: public AudioPlayerInterface
@ -34,12 +30,8 @@ private slots:
private: private:
QPointer< QBuffer > audioBuffer; QPointer< QBuffer > audioBuffer;
QMediaPlayer player; ///< Depends on audioBuffer. QMediaPlayer player; ///< Depends on audioBuffer.
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) )
QAudioOutput audioOutput; QAudioOutput audioOutput;
#endif
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) )
QMediaDevices mediaDevices; QMediaDevices mediaDevices;
#endif
}; };
#endif // MAKE_QTMULTIMEDIA_PLAYER #endif // MAKE_QTMULTIMEDIA_PLAYER

View file

@ -20,41 +20,23 @@ bool isCombiningMark( wchar ch )
wstring apply( wstring const & in, bool preserveWildcards ) wstring apply( wstring const & in, bool preserveWildcards )
{ {
//remove space and accent; // remove diacritics (normalization), white space, punt,
auto withPunc = QString::fromStdU32String( in ) auto temp = QString::fromStdU32String( in )
.normalized( QString::NormalizationForm_KD ) .normalized( QString::NormalizationForm_KD )
.remove( RX::markSpace ) .remove( RX::markSpace )
.toStdU32String(); .removeIf( [ preserveWildcards ]( const QChar & ch ) -> bool {
return ch.isPunct()
//First, strip diacritics and apply ws/punctuation removal && !( preserveWildcards && ( ch == '\\' || ch == '?' || ch == '*' || ch == '[' || ch == ']' ) );
wstring withoutDiacritics; } )
.toStdU32String();
withoutDiacritics.reserve( withPunc.size() ); // case folding
std::u32string caseFolded;
caseFolded.reserve( temp.size() );
for ( auto const & ch : withPunc ) {
if ( !isPunct( ch )
|| ( preserveWildcards && ( ch == '\\' || ch == '?' || ch == '*' || ch == '[' || ch == ']' ) ) ) {
withoutDiacritics.push_back( ch );
}
}
// Now, fold the case
wstring caseFolded;
caseFolded.reserve( withoutDiacritics.size() * foldCaseMaxOut );
wchar const * nextChar = withoutDiacritics.data();
wchar buf[ foldCaseMaxOut ]; wchar buf[ foldCaseMaxOut ];
for ( const char32_t ch : temp ) {
for ( size_t left = withoutDiacritics.size(); left--; ) { auto n = foldCase( ch, buf );
caseFolded.append( buf, foldCase( *nextChar++, buf ) ); caseFolded.append( buf, n );
} }
return caseFolded; return caseFolded;
} }

View file

@ -1,14 +1,10 @@
/* This file is (c) 2013 Abs62 /* This file is (c) 2013 Abs62
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */ * Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include <QString>
#include "gddebug.hh" #include "gddebug.hh"
#include <QDebug> #include <QDebug>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QString>
#include <QtCore5Compat/QTextCodec> #include <QtCore5Compat/QTextCodec>
#else
#include <QTextCodec>
#endif
QFile * logFilePtr; QFile * logFilePtr;

View file

@ -93,7 +93,7 @@ gd::wstring Iconv::toWstring( char const * fromEncoding, void const * fromData,
Iconv ic( fromEncoding ); Iconv ic( fromEncoding );
QString outStr = ic.convert( fromData, dataSize ); QString outStr = ic.convert( fromData, dataSize );
return gd::toWString( outStr ); return outStr.toStdU32String();
} }
std::string Iconv::toUtf8( char const * fromEncoding, void const * fromData, size_t dataSize ) std::string Iconv::toUtf8( char const * fromEncoding, void const * fromData, size_t dataSize )

View file

@ -9,11 +9,6 @@
QString wildcardsToRegexp( const QString & wc_str ) QString wildcardsToRegexp( const QString & wc_str )
{ {
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) // The "anchored" version will enclose the output with \A...\z.
//qt 5.X does not offer an unanchored version. the default output is enclosed between \A \z.
auto anchorPattern = QRegularExpression::wildcardToRegularExpression( wc_str );
return anchorPattern.mid( 2, anchorPattern.length() - 4 );
#else
return QRegularExpression::wildcardToRegularExpression( wc_str, QRegularExpression::UnanchoredWildcardConversion ); return QRegularExpression::wildcardToRegularExpression( wc_str, QRegularExpression::UnanchoredWildcardConversion );
#endif
} }

View file

@ -2,10 +2,6 @@
#include <QList> #include <QList>
namespace gd { namespace gd {
wstring toWString( QString const & in )
{
return in.toStdU32String();
}
// When convert non-BMP characters to wstring,the ending char maybe \0 .This method remove the tailing \0 from the wstring // When convert non-BMP characters to wstring,the ending char maybe \0 .This method remove the tailing \0 from the wstring
// as \0 is sensitive in the index. This method will be only used with index related operations like store/query. // as \0 is sensitive in the index. This method will be only used with index related operations like store/query.
@ -35,7 +31,7 @@ wstring removeTrailingZero( QString const & in )
wstring normalize( const wstring & str ) wstring normalize( const wstring & str )
{ {
return toWString( QString::fromStdU32String( str ).normalized( QString::NormalizationForm_C ) ); return QString::fromStdU32String( str ).normalized( QString::NormalizationForm_C ).toStdU32String();
} }

View file

@ -10,7 +10,6 @@
#include <QString> #include <QString>
namespace gd { namespace gd {
wstring toWString( QString const & );
wstring removeTrailingZero( wstring const & v ); wstring removeTrailingZero( wstring const & v );
wstring removeTrailingZero( QString const & in ); wstring removeTrailingZero( QString const & in );
wstring normalize( wstring const & ); wstring normalize( wstring const & );

View file

@ -201,6 +201,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 ),
@ -929,6 +930,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" );
@ -1866,6 +1872,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

@ -356,6 +356,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;
@ -492,7 +493,7 @@ struct WebSite
QString id, name, url; QString id, name, url;
bool enabled; bool enabled;
QString iconFilename; QString iconFilename;
bool inside_iframe; bool inside_iframe = false;
WebSite(): WebSite():
enabled( false ) enabled( false )
@ -698,7 +699,7 @@ struct Transliteration
struct Lingua struct Lingua
{ {
bool enable; bool enable = false;
QString languageCodes; QString languageCodes;
bool operator==( Lingua const & other ) const bool operator==( Lingua const & other ) const
@ -737,13 +738,16 @@ struct Forvo
struct Program struct Program
{ {
bool enabled; bool enabled;
// NOTE: the value of this enum is used for config
enum Type { enum Type {
Audio, Invalid = -1, // Init value
PlainText, Audio = 0,
Html, PlainText = 1,
PrefixMatch, Html = 2,
MaxTypeValue PrefixMatch = 3,
} type; MaxTypeValue = 4
};
Type type = Invalid;
QString id, name, commandLine; QString id, name, commandLine;
QString iconFilename; QString iconFilename;
@ -892,7 +896,7 @@ struct Class
QString articleSavePath; // Path to save articles QString articleSavePath; // Path to save articles
bool pinPopupWindow; // Last pin status bool pinPopupWindow; // Last pin status
bool popupWindowAlwaysOnTop; // Last status of pinned popup window bool popupWindowAlwaysOnTop = false; // Last status of pinned popup window
QByteArray mainWindowState; // Binary state saved by QMainWindow QByteArray mainWindowState; // Binary state saved by QMainWindow
QByteArray mainWindowGeometry; // Geometry saved by QMainWindow QByteArray mainWindowGeometry; // Geometry saved by QMainWindow
@ -929,7 +933,7 @@ struct Class
Group const * getGroup( unsigned id ) const; Group const * getGroup( unsigned id ) const;
//disable tts dictionary. does not need to save to persistent file //disable tts dictionary. does not need to save to persistent file
bool notts = false; bool notts = false;
bool resetState; bool resetState = false;
}; };
/// Configuration-specific events. Some parts of the program need to react /// Configuration-specific events. Some parts of the program need to react

View file

@ -45,9 +45,9 @@ DEF_EX( exCorruptedChainData, "Corrupted chain data in the leaf of a btree encou
struct WordArticleLink struct WordArticleLink
{ {
string word, prefix; // in utf8 string word, prefix; // in utf8
uint32_t articleOffset; uint32_t articleOffset = 0;
WordArticleLink() {} WordArticleLink() = default;
WordArticleLink( string const & word_, uint32_t articleOffset_, string const & prefix_ = string() ): WordArticleLink( string const & word_, uint32_t articleOffset_, string const & prefix_ = string() ):
word( word_ ), word( word_ ),

View file

@ -424,7 +424,7 @@ public:
if ( countn ) { if ( countn ) {
QMutexLocker _( &dataMutex ); QMutexLocker _( &dataMutex );
for ( int x = 0; x < countn; x++ ) { for ( int x = 0; x < countn; x++ ) {
matches.emplace_back( gd::toWString( matchesList.at( x ) ) ); matches.emplace_back( matchesList.at( x ).toStdU32String() );
} }
} }
finish(); finish();
@ -538,11 +538,13 @@ void DictServerWordSearchRequest::readMatchData( QByteArray & reply )
if ( word.endsWith( '\"' ) ) { if ( word.endsWith( '\"' ) ) {
word.chop( 1 ); word.chop( 1 );
} }
if ( word[ 0 ] == '\"' ) { if ( word.startsWith( '\"' ) ) {
word = word.mid( 1 ); word = word.remove( 0, 1 );
} }
this->addMatchedWord( word ); if ( !word.isEmpty() ) {
this->addMatchedWord( word );
}
} }
reply = this->dictImpl->socket.readLine(); reply = this->dictImpl->socket.readLine();

View file

@ -540,7 +540,7 @@ void DslDictionary::loadArticle( uint32_t address,
if ( !articleBody ) { if ( !articleBody ) {
// throw exCantReadFile( getDictionaryFilenames()[ 0 ] ); // throw exCantReadFile( getDictionaryFilenames()[ 0 ] );
articleData = U"\n\r\t" + gd::toWString( QString( "DICTZIP error: " ) + dict_error_str( dz ) ); articleData = U"\n\r\tDICTZIP error: " + QString( dict_error_str( dz ) ).toStdU32String();
} }
else { else {
try { try {
@ -966,7 +966,7 @@ string DslDictionary::nodeToHtml( ArticleDom::Node const & node )
if ( n >= 0 ) { if ( n >= 0 ) {
int n2 = attr.indexOf( '\"', n + 6 ); int n2 = attr.indexOf( '\"', n + 6 );
if ( n2 > 0 ) { if ( n2 > 0 ) {
quint32 id = dslLanguageToId( gd::toWString( attr.mid( n + 6, n2 - n - 6 ) ) ); quint32 id = dslLanguageToId( attr.mid( n + 6, n2 - n - 6 ).toStdU32String() );
langcode = LangCoder::intToCode2( id ).toStdString(); langcode = LangCoder::intToCode2( id ).toStdString();
} }
} }
@ -1089,7 +1089,7 @@ QString const & DslDictionary::getDescription()
for ( ;; ) { for ( ;; ) {
data.clear(); data.clear();
langStr = str.mid( 10 ).replace( '\"', ' ' ).trimmed(); langStr = str.mid( 10 ).replace( '\"', ' ' ).trimmed();
annLang = LangCoder::findIdForLanguage( gd::toWString( langStr ) ); annLang = LangCoder::findIdForLanguage( langStr.toStdU32String() );
do { do {
str = annStream.readLine(); str = annStream.readLine();
if ( str.left( 10 ).compare( "#LANGUAGE " ) == 0 ) { if ( str.left( 10 ).compare( "#LANGUAGE " ) == 0 ) {
@ -1391,7 +1391,7 @@ void DslDictionary::getArticleText( uint32_t articleAddress, QString & headword,
if ( haveInsidedCards ) { if ( haveInsidedCards ) {
// Use base DSL parser for articles with insided cards // Use base DSL parser for articles with insided cards
ArticleDom dom( gd::toWString( text ), getName(), articleHeadword ); ArticleDom dom( text.toStdU32String(), getName(), articleHeadword );
text = QString::fromStdU32String( dom.root.renderAsText( true ) ); text = QString::fromStdU32String( dom.root.renderAsText( true ) );
} }
else { else {

View file

@ -9,11 +9,7 @@
#include <zlib.h> #include <zlib.h>
#include "dictionary.hh" #include "dictionary.hh"
#include "iconv.hh" #include "iconv.hh"
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QtCore5Compat/QTextCodec>
#include <QtCore5Compat/QTextCodec>
#else
#include <QTextCodec>
#endif
#include <QByteArray> #include <QByteArray>
#include "utf8.hh" #include "utf8.hh"

View file

@ -994,7 +994,7 @@ void EpwingWordSearchRequest::findMatches()
QMutexLocker _( &dataMutex ); QMutexLocker _( &dataMutex );
for ( const auto & headword : headwords ) for ( const auto & headword : headwords )
addMatch( gd::toWString( headword ) ); addMatch( headword.toStdU32String() );
break; break;
} }
@ -1047,7 +1047,7 @@ void addWordToChunks( Epwing::Book::EpwingHeadword & head,
chunks.addToBlock( &head.page, sizeof( head.page ) ); chunks.addToBlock( &head.page, sizeof( head.page ) );
chunks.addToBlock( &head.offset, sizeof( head.offset ) ); chunks.addToBlock( &head.offset, sizeof( head.offset ) );
wstring hw = gd::toWString( head.headword ); wstring hw = head.headword.toStdU32String();
indexedWords.addWord( hw, offset ); indexedWords.addWord( hw, offset );
wordCount++; wordCount++;

View file

@ -541,11 +541,7 @@ bool EpwingBook::setSubBook( int book_nom )
QFile f( fileName ); QFile f( fileName );
if ( f.open( QFile::ReadOnly | QFile::Text ) ) { if ( f.open( QFile::ReadOnly | QFile::Text ) ) {
QTextStream ts( &f ); QTextStream ts( &f );
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
ts.setCodec( "UTF-8" );
#else
ts.setEncoding( QStringConverter::Utf8 ); ts.setEncoding( QStringConverter::Utf8 );
#endif
QString line = ts.readLine(); QString line = ts.readLine();
while ( !line.isEmpty() ) { while ( !line.isEmpty() ) {
@ -1141,7 +1137,7 @@ void EpwingBook::fixHeadword( QString & headword )
// return; // return;
//} //}
gd::wstring folded = Folding::applyPunctOnly( gd::toWString( fixed ) ); gd::wstring folded = Folding::applyPunctOnly( fixed.toStdU32String() );
//fixed = QString::fromStdU32String( folded ); //fixed = QString::fromStdU32String( folded );
//if( isHeadwordCorrect( fixed ) ) //if( isHeadwordCorrect( fixed ) )

View file

@ -16,12 +16,7 @@
#endif #endif
#include <QString> #include <QString>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QtCore5Compat/QTextCodec>
#include <QtCore5Compat/QTextCodec>
#else
#include <QTextCodec>
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
#include <stub_msvc.h> #include <stub_msvc.h>

View file

@ -32,11 +32,8 @@
#include <QBuffer> #include <QBuffer>
#include <QRegularExpression> #include <QRegularExpression>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QtCore5Compat/QTextCodec>
#include <QtCore5Compat/QTextCodec>
#else
#include <QTextCodec>
#endif
#include <string> #include <string>
#include <list> #include <list>
#include <map> #include <map>

View file

@ -413,7 +413,7 @@ QList< wstring > suggest( wstring & word, QMutex & hunspellMutex, Hunspell & hun
auto match = cutStem.match( suggestion.trimmed() ); auto match = cutStem.match( suggestion.trimmed() );
if ( match.hasMatch() ) { if ( match.hasMatch() ) {
wstring alt = gd::toWString( match.captured( 1 ) ); wstring alt = match.captured( 1 ).toStdU32String();
if ( Folding::applySimpleCaseOnly( alt ) != lowercasedWord ) // No point in providing same word if ( Folding::applySimpleCaseOnly( alt ) != lowercasedWord ) // No point in providing same word
{ {
@ -643,7 +643,7 @@ wstring decodeFromHunspell( Hunspell & hunspell, char const * str )
size_t outLeft = result.size() * sizeof( wchar ); size_t outLeft = result.size() * sizeof( wchar );
QString convStr = conv.convert( in, inLeft ); QString convStr = conv.convert( in, inLeft );
return gd::toWString( convStr ); return convStr.toStdU32String();
} }
} // namespace } // namespace

View file

@ -32,11 +32,8 @@
#include <QDomDocument> #include <QDomDocument>
#include <QTextDocumentFragment> #include <QTextDocumentFragment>
#include <QDataStream> #include <QDataStream>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QtCore5Compat/QTextCodec>
#include <QtCore5Compat/QTextCodec>
#else
#include <QTextCodec>
#endif
#include "decompress.hh" #include "decompress.hh"
#include "gddebug.hh" #include "gddebug.hh"
#include "ripemd.hh" #include "ripemd.hh"

View file

@ -785,7 +785,7 @@ void MddResourceRequest::run()
data.push_back( '\0' ); data.push_back( '\0' );
QString target = QString target =
MdictParser::toUtf16( "UTF-16LE", &data.front() + sizeof( pattern ), data.size() - sizeof( pattern ) ); MdictParser::toUtf16( "UTF-16LE", &data.front() + sizeof( pattern ), data.size() - sizeof( pattern ) );
resourceName = gd::toWString( target.trimmed() ); resourceName = target.trimmed().toStdU32String();
continue; continue;
} }
} }
@ -1200,7 +1200,7 @@ QString MdxDictionary::getCachedFileName( QString filename )
data.push_back( '\0' ); data.push_back( '\0' );
QString target = QString target =
MdictParser::toUtf16( "UTF-16LE", &data.front() + sizeof( pattern ), data.size() - sizeof( pattern ) ); MdictParser::toUtf16( "UTF-16LE", &data.front() + sizeof( pattern ), data.size() - sizeof( pattern ) );
resourceName = gd::toWString( target.trimmed() ); resourceName = target.trimmed().toStdU32String();
continue; continue;
} }
break; break;
@ -1249,14 +1249,14 @@ static void addEntryToIndex( QString const & word, uint32_t offset, IndexedWords
{ {
// Strip any leading or trailing whitespaces // Strip any leading or trailing whitespaces
QString wordTrimmed = word.trimmed(); QString wordTrimmed = word.trimmed();
indexedWords.addWord( gd::toWString( wordTrimmed ), offset ); indexedWords.addWord( wordTrimmed.toStdU32String(), offset );
} }
static void addEntryToIndexSingle( QString const & word, uint32_t offset, IndexedWords & indexedWords ) static void addEntryToIndexSingle( QString const & word, uint32_t offset, IndexedWords & indexedWords )
{ {
// Strip any leading or trailing whitespaces // Strip any leading or trailing whitespaces
QString wordTrimmed = word.trimmed(); QString wordTrimmed = word.trimmed();
indexedWords.addSingleWord( gd::toWString( wordTrimmed ), offset ); indexedWords.addSingleWord( wordTrimmed.toStdU32String(), offset );
} }
class ArticleHandler: public MdictParser::RecordHandler class ArticleHandler: public MdictParser::RecordHandler

View file

@ -223,7 +223,7 @@ void MediaWikiWordSearchRequest::downloadFinished()
qDebug() << "matches" << matches.size(); qDebug() << "matches" << matches.size();
for ( int x = 0; x < nl.length(); ++x ) { for ( int x = 0; x < nl.length(); ++x ) {
matches.emplace_back( gd::toWString( nl.item( x ).toElement().attribute( "title" ) ) ); matches.emplace_back( nl.item( x ).toElement().attribute( "title" ).toStdU32String() );
} }
} }
} }

View file

@ -334,7 +334,7 @@ void ProgramWordSearchRequest::instanceFinished( QByteArray output, QString erro
QStringList result = QString::fromUtf8( output ).split( "\n", Qt::SkipEmptyParts ); QStringList result = QString::fromUtf8( output ).split( "\n", Qt::SkipEmptyParts );
for ( const auto & x : result ) { for ( const auto & x : result ) {
matches.push_back( Dictionary::WordMatch( gd::toWString( x ) ) ); matches.push_back( Dictionary::WordMatch( x.toStdU32String() ) );
} }
if ( !error.isEmpty() ) { if ( !error.isEmpty() ) {

View file

@ -1,36 +1,31 @@
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org> /* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */ * Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "sdict.hh"
#include "btreeidx.hh" #include "btreeidx.hh"
#include "folding.hh"
#include "utf8.hh"
#include "chunkedstorage.hh" #include "chunkedstorage.hh"
#include "langcoder.hh"
#include "gddebug.hh"
#include "decompress.hh" #include "decompress.hh"
#include "htmlescape.hh" #include "folding.hh"
#include "ftshelpers.hh" #include "ftshelpers.hh"
#include "gddebug.hh"
#include "htmlescape.hh"
#include "langcoder.hh"
#include "sdict.hh"
#include "utf8.hh"
#include <map> #include <map>
#include <QAtomicInt>
#include <QRegularExpression>
#include <QSemaphore>
#include <QString>
#include <set> #include <set>
#include <string> #include <string>
#include "utils.hh"
#ifdef _MSC_VER #ifdef _MSC_VER
#include <stub_msvc.h> #include <stub_msvc.h>
#endif #endif
#include <QString>
#include <QSemaphore>
#include <QAtomicInt>
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
#include <QtCore5Compat>
#endif
#include <QRegularExpression>
#include "utils.hh"
namespace Sdict { namespace Sdict {
using std::map; using std::map;

View file

@ -1315,10 +1315,10 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
|| contentType.startsWith( "text/plain", Qt::CaseInsensitive ) ) { || contentType.startsWith( "text/plain", Qt::CaseInsensitive ) ) {
//Article //Article
if ( maxHeadwordsToExpand && entries > maxHeadwordsToExpand ) { if ( maxHeadwordsToExpand && entries > maxHeadwordsToExpand ) {
indexedWords.addSingleWord( gd::toWString( refEntry.key ), offsets[ i ].second ); indexedWords.addSingleWord( refEntry.key.toStdU32String(), offsets[ i ].second );
} }
else { else {
indexedWords.addWord( gd::toWString( refEntry.key ), offsets[ i ].second ); indexedWords.addWord( refEntry.key.toStdU32String(), offsets[ i ].second );
} }
wordCount += 1; wordCount += 1;
@ -1330,7 +1330,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
} }
} }
else { else {
indexedResources.addSingleWord( gd::toWString( refEntry.key ), offsets[ i ].second ); indexedResources.addSingleWord( refEntry.key.toStdU32String(), offsets[ i ].second );
} }
} }
sf.clearRefOffsets(); sf.clearRefOffsets();

View file

@ -409,7 +409,7 @@ void addDir( QDir const & baseDir,
const uint32_t articleOffset = chunks.startNewBlock(); const uint32_t articleOffset = chunks.startNewBlock();
chunks.addToBlock( fileName.c_str(), fileName.size() + 1 ); chunks.addToBlock( fileName.c_str(), fileName.size() + 1 );
wstring name = gd::toWString( i->fileName() ); wstring name = i->fileName().toStdU32String();
const wstring::size_type pos = name.rfind( L'.' ); const wstring::size_type pos = name.rfind( L'.' );

View file

@ -8,11 +8,8 @@
#include "utf8.hh" #include "utf8.hh"
#include "iconv.hh" #include "iconv.hh"
#include "wstring_qt.hh" #include "wstring_qt.hh"
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QtCore5Compat/QTextCodec>
#include <QtCore5Compat/QTextCodec>
#else
#include <QTextCodec>
#endif
#include <QMutexLocker> #include <QMutexLocker>
using namespace BtreeIndexing; using namespace BtreeIndexing;
@ -214,7 +211,7 @@ bool IndexedZip::indexFile( BtreeIndexing::IndexedWords & zipFileNames, quint32
// System locale // System locale
if ( localeCodec ) { if ( localeCodec ) {
QString name = localeCodec->toUnicode( entry.fileName.constData(), entry.fileName.size() ); QString name = localeCodec->toUnicode( entry.fileName.constData(), entry.fileName.size() );
nameInSystemLocale = gd::toWString( name ); nameInSystemLocale = name.toStdU32String();
if ( !nameInSystemLocale.empty() ) { if ( !nameInSystemLocale.empty() ) {
zipFileNames.addSingleWord( nameInSystemLocale, entry.localHeaderOffset ); zipFileNames.addSingleWord( nameInSystemLocale, entry.localHeaderOffset );

View file

@ -903,7 +903,7 @@ void indexArticle( GzippedFile & gzFile,
// Add words to index // Add words to index
for ( const auto & word : words ) { for ( const auto & word : words ) {
indexedWords.addWord( gd::toWString( word ), offset ); indexedWords.addWord( word.toStdU32String(), offset );
} }
++articleCount; ++articleCount;
@ -1226,7 +1226,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
while ( !( stream.isEndElement() && stream.name() == u"abbr_def" ) || !stream.atEnd() ) { while ( !( stream.isEndElement() && stream.name() == u"abbr_def" ) || !stream.atEnd() ) {
if ( stream.isStartElement() && stream.name() == u"abbr_k" ) { if ( stream.isStartElement() && stream.name() == u"abbr_k" ) {
s = readElementText( stream ); s = readElementText( stream );
keys.push_back( gd::toWString( s ) ); keys.push_back( s.toStdU32String() );
} }
else if ( stream.isStartElement() && stream.name() == u"abbr_v" ) { else if ( stream.isStartElement() && stream.name() == u"abbr_v" ) {
s = readElementText( stream ); s = readElementText( stream );
@ -1246,7 +1246,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( vector< string > const & f
while ( !( stream.isEndElement() && stream.name() == u"abr_def" ) || !stream.atEnd() ) { while ( !( stream.isEndElement() && stream.name() == u"abr_def" ) || !stream.atEnd() ) {
if ( stream.isStartElement() && stream.name() == u"k" ) { if ( stream.isStartElement() && stream.name() == u"k" ) {
s = readElementText( stream ); s = readElementText( stream );
keys.push_back( gd::toWString( s ) ); keys.push_back( s.toStdU32String() );
} }
else if ( stream.isStartElement() && stream.name() == u"v" ) { else if ( stream.isStartElement() && stream.name() == u"v" ) {
s = readElementText( stream ); s = readElementText( stream );

View file

@ -102,8 +102,7 @@ string convert( string const & in,
} }
break; break;
} }
// Fall-through [[fallthrough]];
default: default:
inConverted.push_back( i ); inConverted.push_back( i );
afterEol = false; afterEol = false;
@ -630,7 +629,7 @@ string convert( string const & in,
// if( type == XDXF && dictPtr != NULL && !el.hasAttribute( "start" ) ) // if( type == XDXF && dictPtr != NULL && !el.hasAttribute( "start" ) )
if ( dictPtr != NULL && !el.hasAttribute( "start" ) ) { if ( dictPtr != NULL && !el.hasAttribute( "start" ) ) {
string filename = Utf8::encode( gd::toWString( el.text() ) ); string filename = Utf8::encode( el.text().toStdU32String() );
if ( Filetype::isNameOfPicture( filename ) ) { if ( Filetype::isNameOfPicture( filename ) ) {
QUrl url; QUrl url;

View file

@ -1,17 +1,10 @@
#ifdef __WIN32 // Q_OS_WIN32 isn't available at this point
#define _WIN32_WINNT 0x0430
#include <windows.h>
#endif
#include "hotkeywrapper.hh" #include "hotkeywrapper.hh"
#include "gddebug.hh" #include "gddebug.hh"
#include <QSessionManager>
#include <QTimer> #include <QTimer>
#include <QSessionManager>
#include <QWidget> #include <QWidget>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "mainwindow.hh" #include <windows.h>
#endif #endif
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -100,11 +93,6 @@ HotkeyStruct::HotkeyStruct( quint32 key_, quint32 key2_, quint32 modifier_, int
modifier( modifier_ ), modifier( modifier_ ),
handle( handle_ ), handle( handle_ ),
id( id_ ) id( id_ )
#ifdef Q_OS_MAC
,
hkRef( 0 ),
hkRef2( 0 )
#endif
{ {
} }
@ -115,14 +103,7 @@ HotkeyWrapper::HotkeyWrapper( QObject * parent ):
QThread( parent ), QThread( parent ),
state2( false ) state2( false )
{ {
#ifdef Q_OS_WIN
hwnd = (HWND)( ( static_cast< QMainWindow * >( parent ) )->winId() );
gdDebug( "Handle global hotkeys via RegisterHotkey()" );
#else
init(); init();
#endif
( static_cast< QHotkeyApplication * >( qApp ) )->registerWrapper( this ); ( static_cast< QHotkeyApplication * >( qApp ) )->registerWrapper( this );
} }
@ -164,7 +145,8 @@ bool HotkeyWrapper::checkState( quint32 vk, quint32 mod )
if ( hs.key == vk && hs.modifier == mod ) { if ( hs.key == vk && hs.modifier == mod ) {
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
// If that was a copy-to-clipboard shortcut, re-emit it back so it could
// reach its original destination so it could be acted upon.
if ( hs.key2 != 0 || ( mod == MOD_CONTROL && ( vk == VK_INSERT || vk == 'c' || vk == 'C' ) ) ) { if ( hs.key2 != 0 || ( mod == MOD_CONTROL && ( vk == VK_INSERT || vk == 'c' || vk == 'C' ) ) ) {
// Pass-through first part of compound hotkey or clipdoard copy command // Pass-through first part of compound hotkey or clipdoard copy command
@ -270,262 +252,8 @@ bool HotkeyWrapper::checkState( quint32 vk, quint32 mod )
return false; return false;
} }
//////////////////////////////////////////////////////////////////////////
#ifdef Q_OS_WIN #ifndef Q_OS_WIN
void HotkeyWrapper::init()
{
QWidget * root = qApp->topLevelWidgets().value( 0 );
hwnd = (HWND)root->winId();
}
bool HotkeyWrapper::setGlobalKey( QKeySequence const & seq, int handle )
{
Config::HotKey hk( seq );
return setGlobalKey( hk.key1, hk.key2, hk.modifiers, handle );
}
bool HotkeyWrapper::setGlobalKey( int key, int key2, Qt::KeyboardModifiers modifier, int handle )
{
if ( !key )
return false; // We don't monitor empty combinations
static int id = 0;
if ( id > 0xBFFF - 1 )
id = 0;
quint32 mod = 0;
if ( modifier & Qt::CTRL )
mod |= MOD_CONTROL;
if ( modifier & Qt::ALT )
mod |= MOD_ALT;
if ( modifier & Qt::SHIFT )
mod |= MOD_SHIFT;
if ( modifier & Qt::META )
mod |= MOD_WIN;
quint32 vk = nativeKey( key );
quint32 vk2 = key2 ? nativeKey( key2 ) : 0;
hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, id ) );
if ( !RegisterHotKey( hwnd, id++, mod, vk ) )
return false;
if ( key2 && key2 != key )
return RegisterHotKey( hwnd, id++, mod, vk2 );
return true;
}
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
bool HotkeyWrapper::winEvent( MSG * message, long * result )
#else
bool HotkeyWrapper::winEvent( MSG * message, qintptr * result )
#endif
{
(void)result;
if ( message->message == WM_HOTKEY )
return checkState( ( message->lParam >> 16 ), ( message->lParam & 0xffff ) );
return false;
}
quint32 HotkeyWrapper::nativeKey( int key )
{
if ( key >= Qt::Key_0 && key <= Qt::Key_9 )
return VK_NUMPAD0 + ( key - Qt::Key_0 );
if ( key >= Qt::Key_A && key <= Qt::Key_Z )
return key;
switch ( key ) {
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Colon:
case Qt::Key_Semicolon:
return VK_OEM_1;
case Qt::Key_Question:
return VK_OEM_2;
case Qt::Key_AsciiTilde:
case Qt::Key_QuoteLeft:
return VK_OEM_3;
case Qt::Key_BraceLeft:
case Qt::Key_BracketLeft:
return VK_OEM_4;
case Qt::Key_Bar:
case Qt::Key_Backslash:
return VK_OEM_5;
case Qt::Key_BraceRight:
case Qt::Key_BracketRight:
return VK_OEM_6;
case Qt::Key_QuoteDbl:
case Qt::Key_Apostrophe:
return VK_OEM_7;
case Qt::Key_Less:
return VK_OEM_COMMA;
case Qt::Key_Greater:
return VK_OEM_PERIOD;
case Qt::Key_Equal:
return VK_OEM_PLUS;
case Qt::Key_ParenRight:
return 0x30;
case Qt::Key_Exclam:
return 0x31;
case Qt::Key_At:
return 0x32;
case Qt::Key_NumberSign:
return 0x33;
case Qt::Key_Dollar:
return 0x34;
case Qt::Key_Percent:
return 0x35;
case Qt::Key_AsciiCircum:
return 0x36;
case Qt::Key_Ampersand:
return 0x37;
case Qt::Key_copyright:
return 0x38;
case Qt::Key_ParenLeft:
return 0x39;
case Qt::Key_Underscore:
return VK_OEM_MINUS;
default:;
}
return key;
}
void HotkeyWrapper::unregister()
{
for ( int i = 0; i < hotkeys.count(); i++ ) {
HotkeyStruct const & hk = hotkeys.at( i );
UnregisterHotKey( hwnd, hk.id );
if ( hk.key2 && hk.key2 != hk.key )
UnregisterHotKey( hwnd, hk.id + 1 );
}
( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this );
}
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, void * message, long * result )
#else
bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, void * message, qintptr * result )
#endif
{
MSG * msg = reinterpret_cast< MSG * >( message );
if ( msg->message == WM_HOTKEY ) {
for ( int i = 0; i < hotkeyWrappers.size(); i++ ) {
if ( hotkeyWrappers.at( i )->winEvent( msg, result ) )
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
#else
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View file

@ -1,5 +1,12 @@
#pragma once #pragma once
/// @file
/// Handling global hotkeys and some tricks
/// Part of this header are implmented in
/// + `winhotkeywrapper.cc`
/// + `machotkeywrapper.hh`
///
#include <QGuiApplication> #include <QGuiApplication>
#include <QThread> #include <QThread>
@ -37,15 +44,17 @@
struct HotkeyStruct struct HotkeyStruct
{ {
HotkeyStruct() {} HotkeyStruct() = default;
HotkeyStruct( quint32 key, quint32 key2, quint32 modifier, int handle, int id ); HotkeyStruct( quint32 key, quint32 key2, quint32 modifier, int handle, int id );
quint32 key, key2; quint32 key = 0;
quint32 modifier; quint32 key2 = 0;
int handle; quint32 modifier = 0;
int id; int handle = 0;
int id = 0;
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
EventHotKeyRef hkRef, hkRef2; EventHotKeyRef hkRef = 0;
EventHotKeyRef hkRef2 = 0;
#endif #endif
}; };
@ -98,15 +107,11 @@ private:
HotkeyStruct state2waiter; HotkeyStruct state2waiter;
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
virtual bool winEvent( MSG * message, long * result );
#else
virtual bool winEvent( MSG * message, qintptr * result ); virtual bool winEvent( MSG * message, qintptr * result );
#endif
HWND hwnd; HWND hwnd;
#endif
#elif defined( Q_OS_MAC ) #ifdef Q_OS_MAC
public: public:
void activated( int hkId ); void activated( int hkId );
@ -117,9 +122,9 @@ private:
static EventHandlerUPP hotKeyFunction; static EventHandlerUPP hotKeyFunction;
quint32 keyC; quint32 keyC;
EventHandlerRef handlerRef; EventHandlerRef handlerRef;
#endif
#else #ifdef HAVE_X11
static void recordEventCallback( XPointer, XRecordInterceptData * ); static void recordEventCallback( XPointer, XRecordInterceptData * );
/// Called by recordEventCallback() /// Called by recordEventCallback()
@ -205,16 +210,10 @@ protected:
void registerWrapper( HotkeyWrapper * wrapper ); void registerWrapper( HotkeyWrapper * wrapper );
void unregisterWrapper( HotkeyWrapper * wrapper ); void unregisterWrapper( HotkeyWrapper * wrapper );
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
virtual bool nativeEventFilter( const QByteArray & eventType, void * message, long * result );
#else
virtual bool nativeEventFilter( const QByteArray & eventType, void * message, qintptr * result ); virtual bool nativeEventFilter( const QByteArray & eventType, void * message, qintptr * result );
#endif #endif
protected:
#endif // Q_OS_WIN32
QList< HotkeyWrapper * > hotkeyWrappers; QList< HotkeyWrapper * > hotkeyWrappers;
}; };

View file

@ -105,15 +105,8 @@ void IframeSchemeHandler::requestStarted( QWebEngineUrlRequestJob * requestJob )
buffer->setData( articleString.toUtf8() ); buffer->setData( articleString.toUtf8() );
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
requestJob->reply( "text/html; charset=utf-8", buffer ); requestJob->reply( "text/html; charset=utf-8", buffer );
#else
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
requestJob->reply( contentType, buffer );
#else
requestJob->reply( "text/html", buffer );
#endif
#endif
}; };
connect( reply, &QNetworkReply::finished, requestJob, finishAction ); connect( reply, &QNetworkReply::finished, requestJob, finishAction );

View file

@ -307,15 +307,7 @@ void processCommandLine( QCoreApplication * app, GDOptions * result )
// Handle cases where we get encoded URL // Handle cases where we get encoded URL
if ( result->word.startsWith( QStringLiteral( "xn--" ) ) ) { if ( result->word.startsWith( QStringLiteral( "xn--" ) ) ) {
// For `kde-open` or `gio` or others, URL are encoded into ACE or Punycode // For `kde-open` or `gio` or others, URL are encoded into ACE or Punycode
#if QT_VERSION >= QT_VERSION_CHECK( 6, 3, 0 )
result->word = QUrl::fromAce( result->word.toLatin1(), QUrl::IgnoreIDNWhitelist ); result->word = QUrl::fromAce( result->word.toLatin1(), QUrl::IgnoreIDNWhitelist );
#else
// Old Qt's fromAce only applies to whitelisted domains, so we add .com to bypass this restriction :)
// https://bugreports.qt.io/browse/QTBUG-29080
result->word.append( QStringLiteral( ".com" ) );
result->word = QUrl::fromAce( result->word.toLatin1() );
result->word.chop( 4 );
#endif
} }
else if ( result->word.startsWith( QStringLiteral( "%" ) ) ) { else if ( result->word.startsWith( QStringLiteral( "%" ) ) ) {
// For Firefox or other browsers where URL are percent encoded // For Firefox or other browsers where URL are percent encoded
@ -349,8 +341,11 @@ int main( int argc, char ** argv )
// attach the new console to this application's process // attach the new console to this application's process
if ( AttachConsole( ATTACH_PARENT_PROCESS ) ) { if ( AttachConsole( ATTACH_PARENT_PROCESS ) ) {
// reopen the std I/O streams to redirect I/O to the new console // reopen the std I/O streams to redirect I/O to the new console
freopen( "CON", "w", stdout ); auto ret1 = freopen( "CON", "w", stdout );
freopen( "CON", "w", stderr ); auto ret2 = freopen( "CON", "w", stderr );
if ( ret1 == nullptr || ret2 == nullptr ) {
qDebug() << "Attaching console stdout or stderr failed";
}
} }
qputenv( "QT_QPA_PLATFORM", "windows:darkmode=1" ); qputenv( "QT_QPA_PLATFORM", "windows:darkmode=1" );
@ -359,10 +354,6 @@ int main( int argc, char ** argv )
//high dpi screen support //high dpi screen support
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
QApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
QApplication::setAttribute( Qt::AA_UseHighDpiPixmaps );
#endif
qputenv( "QT_ENABLE_HIGHDPI_SCALING", "1" ); qputenv( "QT_ENABLE_HIGHDPI_SCALING", "1" );
QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::PassThrough ); QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::PassThrough );

View file

@ -1,8 +1,6 @@
#include "article_inspect.hh" #include "article_inspect.hh"
#include <QCloseEvent> #include <QCloseEvent>
#if ( QT_VERSION > QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QWebEngineContextMenuRequest>
#include <QWebEngineContextMenuRequest>
#endif
ArticleInspector::ArticleInspector( QWidget * parent ): ArticleInspector::ArticleInspector( QWidget * parent ):
QWidget( parent, Qt::WindowType::Window ) QWidget( parent, Qt::WindowType::Window )
{ {
@ -39,9 +37,7 @@ void ArticleInspector::triggerAction( QWebEnginePage * page )
return; return;
} }
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) || QT_VERSION > QT_VERSION_CHECK( 6, 3, 0 ) )
page->triggerAction( QWebEnginePage::InspectElement ); page->triggerAction( QWebEnginePage::InspectElement );
#endif
} }
void ArticleInspector::closeEvent( QCloseEvent * ) void ArticleInspector::closeEvent( QCloseEvent * )

View file

@ -31,15 +31,9 @@
#include <map> #include <map>
#include <QApplication> #include <QApplication>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QWebEngineContextMenuRequest>
#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) #include <QWebEngineFindTextResult>
#include <QWebEngineContextMenuData> #include <utility>
#endif
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
#include <QWebEngineContextMenuRequest>
#include <QWebEngineFindTextResult>
#include <utility>
#endif
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
#include <windows.h> #include <windows.h>
#include <QPainter> #include <QPainter>
@ -214,15 +208,7 @@ ArticleView::ArticleView( QWidget * parent,
QWebEngineSettings * settings = webview->settings(); QWebEngineSettings * settings = webview->settings();
settings->setUnknownUrlSchemePolicy( QWebEngineSettings::UnknownUrlSchemePolicy::DisallowUnknownUrlSchemes ); settings->setUnknownUrlSchemePolicy( QWebEngineSettings::UnknownUrlSchemePolicy::DisallowUnknownUrlSchemes );
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
settings->defaultSettings()->setAttribute( QWebEngineSettings::LocalContentCanAccessRemoteUrls, true );
settings->defaultSettings()->setAttribute( QWebEngineSettings::LocalContentCanAccessFileUrls, true );
settings->defaultSettings()->setAttribute( QWebEngineSettings::ErrorPageEnabled, false );
settings->defaultSettings()->setAttribute( QWebEngineSettings::LinksIncludedInFocusChain, false );
settings->defaultSettings()->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false );
settings->defaultSettings()->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true );
settings->defaultSettings()->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false );
#else
settings->setAttribute( QWebEngineSettings::LocalContentCanAccessRemoteUrls, true ); settings->setAttribute( QWebEngineSettings::LocalContentCanAccessRemoteUrls, true );
settings->setAttribute( QWebEngineSettings::LocalContentCanAccessFileUrls, true ); settings->setAttribute( QWebEngineSettings::LocalContentCanAccessFileUrls, true );
settings->setAttribute( QWebEngineSettings::ErrorPageEnabled, false ); settings->setAttribute( QWebEngineSettings::ErrorPageEnabled, false );
@ -230,7 +216,6 @@ ArticleView::ArticleView( QWidget * parent,
settings->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false ); settings->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false );
settings->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true ); settings->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true );
settings->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false ); settings->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false );
#endif
auto html = articleNetMgr.getHtml( ResourceType::UNTITLE ); auto html = articleNetMgr.getHtml( ResourceType::UNTITLE );
@ -1329,12 +1314,10 @@ void ArticleView::print( QPrinter * printer ) const
result = success; result = success;
loop.quit(); loop.quit();
}; };
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
webview->page()->print( printer, std::move( printPreview ) );
#else
connect( webview, &QWebEngineView::printFinished, &loop, std::move( printPreview ) ); connect( webview, &QWebEngineView::printFinished, &loop, std::move( printPreview ) );
webview->print( printer ); webview->print( printer );
#endif
loop.exec(); loop.exec();
if ( !result ) { if ( !result ) {
qDebug() << "print failed"; qDebug() << "print failed";
@ -1363,11 +1346,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
QAction * saveSoundAction = nullptr; QAction * saveSoundAction = nullptr;
QAction * saveBookmark = nullptr; QAction * saveBookmark = nullptr;
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
const QWebEngineContextMenuData * menuData = &( r->contextMenuData() );
#else
QWebEngineContextMenuRequest * menuData = webview->lastContextMenuRequest(); QWebEngineContextMenuRequest * menuData = webview->lastContextMenuRequest();
#endif
QUrl targetUrl( menuData->linkUrl() ); QUrl targetUrl( menuData->linkUrl() );
Contexts contexts; Contexts contexts;
@ -1392,12 +1371,8 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
} }
QUrl imageUrl; QUrl imageUrl;
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
if ( !popupView && menuData->mediaType() == QWebEngineContextMenuData::MediaTypeImage ) if ( !popupView && menuData->mediaType() == QWebEngineContextMenuRequest::MediaType::MediaTypeImage ) {
#else
if ( !popupView && menuData->mediaType() == QWebEngineContextMenuRequest::MediaType::MediaTypeImage )
#endif
{
imageUrl = menuData->mediaUrl(); imageUrl = menuData->mediaUrl();
if ( !imageUrl.isEmpty() ) { if ( !imageUrl.isEmpty() ) {
menu.addAction( webview->pageAction( QWebEnginePage::CopyImageToClipboard ) ); menu.addAction( webview->pageAction( QWebEnginePage::CopyImageToClipboard ) );
@ -1905,7 +1880,7 @@ void ArticleView::doubleClicked( QPoint pos )
emit sendWordToInputLine( selectedText ); emit sendWordToInputLine( selectedText );
// Do some checks to make sure there's a sensible selection indeed // Do some checks to make sure there's a sensible selection indeed
if ( Folding::applyWhitespaceOnly( gd::toWString( selectedText ) ).size() && selectedText.size() < 60 ) { if ( Folding::applyWhitespaceOnly( selectedText.toStdU32String() ).size() && selectedText.size() < 60 ) {
// Initiate translation // Initiate translation
Qt::KeyboardModifiers kmod = QApplication::keyboardModifiers(); Qt::KeyboardModifiers kmod = QApplication::keyboardModifiers();
if ( kmod & ( Qt::ControlModifier | Qt::ShiftModifier ) ) { // open in new tab if ( kmod & ( Qt::ControlModifier | Qt::ShiftModifier ) ) { // open in new tab
@ -1955,19 +1930,12 @@ void ArticleView::findText( QString & text,
const QWebEnginePage::FindFlags & f, const QWebEnginePage::FindFlags & f,
const std::function< void( bool match ) > & callback ) const std::function< void( bool match ) > & callback )
{ {
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
webview->findText( text, f, [ callback ]( const QWebEngineFindTextResult & result ) { webview->findText( text, f, [ callback ]( const QWebEngineFindTextResult & result ) {
auto r = result.numberOfMatches() > 0; auto r = result.numberOfMatches() > 0;
if ( callback ) { if ( callback ) {
callback( r ); callback( r );
} }
} ); } );
#else
webview->findText( text, f, [ callback ]( bool result ) {
if ( callback )
callback( result );
} );
#endif
} }
bool ArticleView::closeSearch() bool ArticleView::closeSearch()
@ -2099,7 +2067,6 @@ void ArticleView::performFtsFindOperation( bool backwards )
QWebEnginePage::FindFlags flags( 0 ); QWebEnginePage::FindFlags flags( 0 );
if ( backwards ) { if ( backwards ) {
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
webview->findText( firstAvailableText, webview->findText( firstAvailableText,
flags | QWebEnginePage::FindBackward, flags | QWebEnginePage::FindBackward,
[ this ]( const QWebEngineFindTextResult & result ) { [ this ]( const QWebEngineFindTextResult & result ) {
@ -2114,16 +2081,8 @@ void ArticleView::performFtsFindOperation( bool backwards )
ftsSearchPanel->statusLabel->setText( ftsSearchPanel->statusLabel->setText(
searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) ); searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) );
} ); } );
#else
webview->findText( firstAvailableText, flags | QWebEnginePage::FindBackward, [ this ]( bool res ) {
ftsSearchPanel->previous->setEnabled( res );
if ( !ftsSearchPanel->next->isEnabled() )
ftsSearchPanel->next->setEnabled( res );
} );
#endif
} }
else { else {
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
webview->findText( firstAvailableText, flags, [ this ]( const QWebEngineFindTextResult & result ) { webview->findText( firstAvailableText, flags, [ this ]( const QWebEngineFindTextResult & result ) {
if ( result.numberOfMatches() == 0 ) { if ( result.numberOfMatches() == 0 ) {
return; return;
@ -2135,15 +2094,6 @@ void ArticleView::performFtsFindOperation( bool backwards )
ftsSearchPanel->statusLabel->setText( searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) ); ftsSearchPanel->statusLabel->setText( searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) );
} ); } );
#else
webview->findText( firstAvailableText, flags, [ this ]( bool res ) {
ftsSearchPanel->next->setEnabled( res );
if ( !ftsSearchPanel->previous->isEnabled() )
ftsSearchPanel->previous->setEnabled( res );
} );
#endif
} }
} }

View file

@ -420,10 +420,6 @@ void DictHeadwords::saveHeadersToFile()
// Write UTF-8 BOM // Write UTF-8 BOM
QTextStream out( &file ); QTextStream out( &file );
out.setGenerateByteOrderMark( true ); out.setGenerateByteOrderMark( true );
//qt 6 will use utf-8 default.
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
out.setCodec( "UTF-8" );
#endif
exportAllWords( progress, out ); exportAllWords( progress, out );

View file

@ -84,13 +84,13 @@ private slots:
private: private:
virtual bool eventFilter( QObject *, QEvent * ); virtual bool eventFilter( QObject *, QEvent * );
Config::Class * m_cfg; Config::Class * m_cfg = nullptr;
QTreeView * m_favoritesTree; QTreeView * m_favoritesTree = nullptr;
QMenu * m_favoritesMenu; QMenu * m_favoritesMenu = nullptr;
QAction * m_deleteSelectedAction; QAction * m_deleteSelectedAction = nullptr;
QAction * m_separator; QAction * m_separator = nullptr;
QAction * m_copySelectedToClipboard; QAction * m_copySelectedToClipboard = nullptr;
QAction * m_addFolder; QAction * m_addFolder = nullptr;
QWidget favoritesPaneTitleBar; QWidget favoritesPaneTitleBar;
QHBoxLayout favoritesPaneTitleBarLayout; QHBoxLayout favoritesPaneTitleBarLayout;

View file

@ -53,13 +53,13 @@ private slots:
private: private:
virtual bool eventFilter( QObject *, QEvent * ); virtual bool eventFilter( QObject *, QEvent * );
Config::Class * m_cfg; Config::Class * m_cfg = nullptr;
History * m_history; History * m_history = nullptr;
QListView * m_historyList; QListView * m_historyList = nullptr;
QMenu * m_historyMenu; QMenu * m_historyMenu = nullptr;
QAction * m_deleteSelectedAction; QAction * m_deleteSelectedAction = nullptr;
QAction * m_separator; QAction * m_separator = nullptr;
QAction * m_copySelectedToClipboard; QAction * m_copySelectedToClipboard = nullptr;
QWidget historyPaneTitleBar; QWidget historyPaneTitleBar;
QHBoxLayout historyPaneTitleBarLayout; QHBoxLayout historyPaneTitleBarLayout;

View file

@ -760,11 +760,7 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
#if defined( Q_OS_LINUX ) #if defined( Q_OS_LINUX )
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
defaultInterfaceStyle = QApplication::style()->name(); defaultInterfaceStyle = QApplication::style()->name();
#else
defaultInterfaceStyle = QApplication::style()->objectName();
#endif
#elif defined( Q_OS_MAC ) #elif defined( Q_OS_MAC )
defaultInterfaceStyle = "Fusion"; defaultInterfaceStyle = "Fusion";
#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">

View file

@ -8,11 +8,6 @@
#include <QBitmap> #include <QBitmap>
#include <QMenu> #include <QMenu>
#include <QMouseEvent> #include <QMouseEvent>
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
#include <QDesktopWidget>
#include <QScreen>
#include <QStringList>
#endif
#include "gddebug.hh" #include "gddebug.hh"
#include "gestures.hh" #include "gestures.hh"
@ -846,11 +841,7 @@ void ScanPopup::leaveEvent( QEvent * event )
} }
} }
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
void ScanPopup::enterEvent( QEnterEvent * event ) void ScanPopup::enterEvent( QEnterEvent * event )
#else
void ScanPopup::enterEvent( QEvent * event )
#endif
{ {
QMainWindow::enterEvent( event ); QMainWindow::enterEvent( event );

View file

@ -174,11 +174,7 @@ private:
virtual void mouseMoveEvent( QMouseEvent * ); virtual void mouseMoveEvent( QMouseEvent * );
virtual void mouseReleaseEvent( QMouseEvent * ); virtual void mouseReleaseEvent( QMouseEvent * );
virtual void leaveEvent( QEvent * event ); virtual void leaveEvent( QEvent * event );
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
virtual void enterEvent( QEnterEvent * event ); virtual void enterEvent( QEnterEvent * event );
#else
virtual void enterEvent( QEvent * event );
#endif
virtual void showEvent( QShowEvent * ); virtual void showEvent( QShowEvent * );
virtual void closeEvent( QCloseEvent * ); virtual void closeEvent( QCloseEvent * );
virtual void moveEvent( QMoveEvent * ); virtual void moveEvent( QMoveEvent * );

View file

@ -0,0 +1,256 @@
#include <QtGlobal>
#ifdef Q_OS_WIN
#include "hotkeywrapper.hh"
#include <windows.h>
#include <QWidget>
/// Implementation is pretty much using RegisterHotKey & UnregisterHotKey
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey
void HotkeyWrapper::init()
{
hwnd = (HWND)( ( static_cast< QWidget * >( this->parent() ) )->winId() );
}
bool HotkeyWrapper::setGlobalKey( QKeySequence const & seq, int handle )
{
Config::HotKey hk( seq );
return setGlobalKey( hk.key1, hk.key2, hk.modifiers, handle );
}
bool HotkeyWrapper::setGlobalKey( int key, int key2, Qt::KeyboardModifiers modifier, int handle )
{
if ( !key )
return false; // We don't monitor empty combinations
static int id = 0;
if ( id > 0xBFFF - 1 )
id = 0;
quint32 mod = 0;
if ( modifier & Qt::CTRL )
mod |= MOD_CONTROL;
if ( modifier & Qt::ALT )
mod |= MOD_ALT;
if ( modifier & Qt::SHIFT )
mod |= MOD_SHIFT;
if ( modifier & Qt::META )
mod |= MOD_WIN;
quint32 vk = nativeKey( key );
quint32 vk2 = key2 ? nativeKey( key2 ) : 0;
hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, id ) );
if ( !RegisterHotKey( hwnd, id++, mod, vk ) )
return false;
if ( key2 && key2 != key )
return RegisterHotKey( hwnd, id++, mod, vk2 );
return true;
}
bool HotkeyWrapper::winEvent( MSG * message, qintptr * result )
{
Q_UNUSED( result );
if ( message->message == WM_HOTKEY )
return checkState( ( message->lParam >> 16 ), ( message->lParam & 0xffff ) );
return false;
}
void HotkeyWrapper::unregister()
{
for ( int i = 0; i < hotkeys.count(); i++ ) {
HotkeyStruct const & hk = hotkeys.at( i );
UnregisterHotKey( hwnd, hk.id );
if ( hk.key2 && hk.key2 != hk.key )
UnregisterHotKey( hwnd, hk.id + 1 );
}
( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this );
}
bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, void * message, qintptr * result )
{
MSG * msg = reinterpret_cast< MSG * >( message );
if ( msg->message == WM_HOTKEY ) {
for ( int i = 0; i < hotkeyWrappers.size(); i++ ) {
if ( hotkeyWrappers.at( i )->winEvent( msg, result ) )
return true;
}
}
return false;
}
/// References:
/// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
/// https://doc.qt.io/qt-6/qt.html#Key-enum
quint32 HotkeyWrapper::nativeKey( int key )
{
// Qt's 0-9 & A-Z overlaps with Windows's VK
if ( key >= Qt::Key_0 && key <= Qt::Key_9 || key >= Qt::Key_A && key <= Qt::Key_Z ) {
return key;
}
switch ( key ) {
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Colon:
case Qt::Key_Semicolon:
return VK_OEM_1;
case Qt::Key_Question:
return VK_OEM_2;
case Qt::Key_AsciiTilde:
case Qt::Key_QuoteLeft:
return VK_OEM_3;
case Qt::Key_BraceLeft:
case Qt::Key_BracketLeft:
return VK_OEM_4;
case Qt::Key_Bar:
case Qt::Key_Backslash:
return VK_OEM_5;
case Qt::Key_BraceRight:
case Qt::Key_BracketRight:
return VK_OEM_6;
case Qt::Key_QuoteDbl:
case Qt::Key_Apostrophe:
return VK_OEM_7;
case Qt::Key_Less:
return VK_OEM_COMMA;
case Qt::Key_Greater:
return VK_OEM_PERIOD;
case Qt::Key_Equal:
return VK_OEM_PLUS;
case Qt::Key_ParenRight:
return 0x30;
case Qt::Key_Exclam:
return 0x31;
case Qt::Key_At:
return 0x32;
case Qt::Key_NumberSign:
return 0x33;
case Qt::Key_Dollar:
return 0x34;
case Qt::Key_Percent:
return 0x35;
case Qt::Key_AsciiCircum:
return 0x36;
case Qt::Key_Ampersand:
return 0x37;
case Qt::Key_copyright:
return 0x38;
case Qt::Key_ParenLeft:
return 0x39;
case Qt::Key_Underscore:
return VK_OEM_MINUS;
case Qt::Key_Meta:
return VK_LWIN;
default:;
}
return key;
}
#endif

View file

@ -131,7 +131,7 @@ void WordFinder::startSearch()
allWordWritings.resize( 1 ); allWordWritings.resize( 1 );
} }
allWordWritings[ 0 ] = gd::toWString( inputWord ); allWordWritings[ 0 ] = inputWord.toStdU32String();
for ( const auto & inputDict : *inputDicts ) { for ( const auto & inputDict : *inputDicts ) {
vector< wstring > writings = inputDict->getAlternateWritings( allWordWritings[ 0 ] ); vector< wstring > writings = inputDict->getAlternateWritings( allWordWritings[ 0 ] );

View file

@ -3,10 +3,10 @@
With venv With venv
``` ```
cd ./website cd ./website
python -m venv ./venv/ python3 -m venv ./venv/
source ./venv/bin/activate source ./venv/bin/activate
# source ./venv/bin/activate.fish # source ./venv/bin/activate.fish
pip install mkdocs-material pip3 install mkdocs-material
``` ```
Then Then

View file

@ -46,3 +46,5 @@ will disable the current dictionary's full-text search.
You can check the full-text search status on each dictionary's info dialog. You can check the full-text search status on each dictionary's info dialog.
![](img/dictionary-info-fullindex.png) ![](img/dictionary-info-fullindex.png)
Note that it is possible to enable full text for a single dictionary by disabling full-text search in the Preferences dialog, and set `fts=true` for that dictionary.

View file

@ -1,4 +1,4 @@
Dictionaries management dialog can be opened via menu `Edit` -> `Dictionaries`. Dictionary management dialog can be opened via menu `Edit` -> `Dictionaries`.
To use local dictionaries, add them via `Sources` -> `Files`. To use local dictionaries, add them via `Sources` -> `Files`.
@ -34,7 +34,7 @@ Morphology dictionary uses Hunspell's morphological analysis to obtain word's va
You can specify a path that includes Hunspell format data files (`.aff` + `.dic`). GoldenDict scan this folder and create a list of available dictionaries. You can specify a path that includes Hunspell format data files (`.aff` + `.dic`). GoldenDict scan this folder and create a list of available dictionaries.
One possible source of Hunspell dictionaries is Libreoffice's [dictionaries](https://github.com/LibreOffice/dictionaries). One possible source of Hunspell dictionaries is LibreOffice's [dictionaries](https://github.com/LibreOffice/dictionaries).
The detailed document about the affix file (`.aff`) and the dict file (`.dic`) can be found at [hunspell.5](https://man.archlinux.org/man/hunspell.5.en). The detailed document about the affix file (`.aff`) and the dict file (`.dic`) can be found at [hunspell.5](https://man.archlinux.org/man/hunspell.5.en).

View file

@ -4,6 +4,7 @@ By creating `article-style.css` or `article-script.js` in GoldenDict's configura
. <- GD's configuration folder . <- GD's configuration folder
├── config ├── config
├── article-style.css ├── article-style.css
├── article-style-print.css (affecting styles when printing)
├── article-script.js ├── article-script.js
└── qt-style.css └── qt-style.css
``` ```

View file

@ -54,17 +54,3 @@
| F3 | Dictionaries dialog | | F3 | Dictionaries dialog |
| F4 | GoldenDict preferences | | F4 | GoldenDict preferences |
| F12 | Inspector | | F12 | Inspector |
# Solo mode in the dictionary bar
Ctrl+Click, Enter solo mode, toggle between single & all dictionaries
Shift+Click, Exit solo mode, restore the previous dictionaries.
For example, there are 4 dictionaries A,B,C,D with ABC selected.
| Cases| Note|
|--------|--------|
| Ctrl+Click A|select A only|
| Ctrl+Click A, Ctrl+Click B | select B only|
| Ctrl+Click A, Ctrl+Click A | A,B,C,D selected(all dictionaries selected)|
| Ctrl+Click A, Shift+Click any dictionary| A,B,C selected |

View file

@ -1,37 +1,56 @@
## Toolbar ## Toolbar
![toolbar](img/toolbar.webp) ![toolbar](img/toolbar.webp)
From left to right: Type your word in Search Box and press `Enter` to search word in the current selected group. You can also choose a variant from a matches list.
* Forward/Backward navigation buttons; Holding Ctrl or Shift will display the translation result in a new tab.
* Group selector
* Search Line
* Toggle Scanning
* Play the first pronunciation in found articles;
* Font scale buttons;
* Save the article as HTML
* Print article
* Add to Favorites;
Type your word in Search Box and press `Enter` to search word in the current selected group. You can also choose a variant from matches list.
If Ctrl or Shift key has been pressed the new tab for translation will be created.
### Wildcard matching ### Wildcard matching
The Search Line can contain wildcard symbols `?` (matches any one character), `*` (matches any characters number) or ranges of characters `[...]`. To find characters `?`, `*`, `[` and `]` it should be escaped with backslash like `\?`, `\*`, `\[`, `\]`. The search line can use wildcard or glob symbols for matching words.
| Wildcard | Description |
|----------|------------------------------------------------------------------------|
| `?` | Matches any single character. |
| `*` | Matches zero or more of any characters. |
| `[abc]` | Matches one character given in the bracket. |
| `[a-c]` | Matches one character from the range given in the bracket. |
| `[!abc]` | Matches one character that is not given in the bracket. |
| `[!a-c]` | Matches one character that is not from the range given in the bracket. |
| `\` | Escaping wildcard symbols, e.g. `\?` to search `?` |
!!! note !!! note
The wildcard symbol at first position in word leads to full headwords list scanning and it may take a long time for huge dictionaries. The wildcard symbol in the first character leads to scanning of every dictionary's every word and may take a long time.
More information about wildcard matching can be found in [Wikipedia's glob article](https://en.wikipedia.org/wiki/Glob_(programming)).
## Dictionary Bar ## Dictionary Bar
The dictionaries bar contains all dictionaries from current dictionaries group. Click the icons to disable/enable them. The dictionary bar contains all dictionaries from the current dictionaries group. Click the icons to disable/enable them.
Hold `Shift` and click dictionary bar will temporally focus on a single dictionary. Hold `Shift` and click again to defocus and restore the previous dictionaries bar state. ### "Solo" mode
Hold `Ctrl` and click dictionary bar will toggle between "Enable a single dictionary" and "Enable all dictionary". Temporally focus on a single dictionary and restore back to all dictionaries or previously selected dictionaries.
Note: The `Shift` and `Ctrl` interaction can also be used on "Found in dictionaries" panel To enter solo mode:
++ctrl+left-button++ -> Select a single dictionary.
To exit solo mode:
++ctrl+left-button++ -> Reselect all dictionaries.
++shift+left-button++ -> Reselect dictionaries that were previously selected before entering solo mode.
For example, there are 4 dictionaries A,B,C,D with ABC selected.
| Cases | Note |
|------------------------------------------|----------------------------------------------|
| Ctrl+Click A | select A only |
| Ctrl+Click A, Ctrl+Click B | select B only |
| Ctrl+Click A, Ctrl+Click A | A,B,C,D selected (all dictionaries selected) |
| Ctrl+Click A, Shift+Click any dictionary | A,B,C selected |
Note: This can also be used on the "Found in dictionaries" panel.

View file

@ -21,6 +21,7 @@ markdown_extensions:
- admonition - admonition
- pymdownx.details - pymdownx.details
- pymdownx.superfences - pymdownx.superfences
- pymdownx.keys
nav: nav:
- Getting started: index.md - Getting started: index.md
@ -33,7 +34,7 @@ nav:
- Popup Window: ui_popup.md - Popup Window: ui_popup.md
- Headwords Dialog: ui_headwords.md - Headwords Dialog: ui_headwords.md
- Full Text Search: ui_fulltextsearch.md - Full Text Search: ui_fulltextsearch.md
- ToolBar & DictBar: ui_toolbar.md - Tool & Dictionary Bar: ui_toolbar.md
- Favorites: ui_favorites.md - Favorites: ui_favorites.md
- Shortcuts: ui_shortcuts.md - Shortcuts: ui_shortcuts.md
- Advanced Usages: - Advanced Usages: