Compare commits

...

28 commits

Author SHA1 Message Date
xiaoyifang 1bd4bf58e3
Merge 3c5233f2a1 into c5d682c993 2024-11-15 16:32:30 +08: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
shenleban tongying 453948155a
clean: simplify macOS hotkey mapping code
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-12 12:33:28 +00:00
shenleban tongying 8fc71c9586
dev: .mm / obj-c files -> clang-format and simplify includes
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-12 10:19:59 +00:00
shenleban tongying 8fd5b37335 clean: remove unused DEF_EX
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-12 01:29:12 -05:00
shenleban tongying 4758f9e972 refactor: change DEF_EX macro's underlying implementation to template 2024-11-12 00:30:02 -05:00
shenleban tongying 608016208b
disable more staffs related to macOS trayicon -> dock icon
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-11 07:53:10 +00:00
shenleban tongying 1e3b22ebd0 fix: macOS -> replace the tray icon with a dock menu 2024-11-11 02:32:27 -05:00
xiaoyifang 3c5233f2a1 1 2024-11-11 09:19:29 +08:00
YiFang Xiao c5ca1b7d63 1 2024-11-11 09:16:14 +08:00
YiFang Xiao 5092ebe2ee 1 2024-11-11 09:16:14 +08:00
YiFang Xiao 5bef4cef22 1 2024-11-11 09:16:14 +08:00
xiaoyifang 59d01868da 1: do not update name 2024-11-11 09:16:14 +08:00
xiaoyifang 10b0496cce 1: do not update name 2024-11-11 09:16:14 +08:00
xiaoyifang 1cf495e7dd Revert "opt: update name should reflect the latest info"
This reverts commit 1272ea2d20a7908b9a69d6b551511916f97cc024.
2024-11-11 09:16:14 +08:00
xiaoyifang c2fc90801b opt: update name should reflect the latest info 2024-11-11 09:16:14 +08:00
autofix-ci[bot] 2de2141758 [autofix.ci] apply automated fixes 2024-11-11 09:16:14 +08:00
xiaoyifang db4c352d6c opt: erase the need to removeTabs 2024-11-11 09:16:14 +08:00
xiaoyifang 7c32cad65a opt: erase the need to removeTabs 2024-11-11 09:16:14 +08:00
70 changed files with 1111 additions and 1291 deletions

View file

@ -142,4 +142,7 @@ StatementMacros:
- QT_REQUIRE_VERSION - QT_REQUIRE_VERSION
UseCRLF: false UseCRLF: false
UseTab: Never UseTab: Never
---
Language: ObjC
BasedOnStyle: WebKit
... ...

View file

@ -17,7 +17,6 @@ Checks: >
portability-*, portability-*,
readability-*, readability-*,
-bugprone-easily-swappable-parameters, -bugprone-easily-swappable-parameters,
-bugprone-reserved-identifier,
-cppcoreguidelines-owning-memory, -cppcoreguidelines-owning-memory,
-cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-prefer-member-initializer,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
@ -44,5 +43,11 @@ CheckOptions:
value: 1 value: 1
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
value: 1 value: 1
- key: modernize-avoid-c-arrays.AllowStringArrays
value: 1
- key: cppcoreguidelines-avoid-c-arrays.AllowStringArrays
value: 1
- key: hicpp-avoid-c-arrays.AllowStringArrays
value: 1
... ...

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

@ -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

@ -4,22 +4,17 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fmt/format.h>
// clang-format off
/// A way to declare an exception class fast /// A way to declare an exception class fast
/// Do like this: /// Do like this:
/// DEF_EX( exErrorInFoo, "An error in foo encountered", std::exception ) /// DEF_EX( exErrorInFoo, "An error in foo encountered", std::exception )
/// DEF_EX( exFooNotFound, "Foo was not found", exErrorInFoo ) /// DEF_EX( exFooNotFound, "Foo was not found", exErrorInFoo )
#define DEF_EX( exName, exDescription, exParent ) \
#define DEF_EX( exName, exDescription, exParent ) \ constexpr static char ExStr_## exName[] = exDescription; \
class exName: public exParent \ using exName = defineEx< exParent, ExStr_## exName >;
{ \
public: \
virtual const char * what() const noexcept \
{ \
return ( exDescription ); \
} \
virtual ~exName() noexcept {} \
};
/// Same as DEF_EX, but takes a runtime string argument, which gets concatenated /// Same as DEF_EX, but takes a runtime string argument, which gets concatenated
/// with the description. /// with the description.
@ -29,20 +24,36 @@
/// throw exCantOpen( "example.txt" ); /// throw exCantOpen( "example.txt" );
/// ///
/// what() would return "can't open file example.txt" /// what() would return "can't open file example.txt"
///
#define DEF_EX_STR( exName, exDescription, exParent ) \
constexpr static char ExStr_## exName[] = exDescription; \
using exName = defineExStr< exParent, ExStr_## exName >;
#define DEF_EX_STR( exName, exDescription, exParent ) \ // clang-format on
class exName: public exParent \
{ \ template< typename ParentEx, const char * description >
std::string value; \ class defineEx: public ParentEx
\ {
public: \ public:
explicit exName( std::string const & value_ ): \ virtual const char * what() const noexcept
value( std::string( exDescription ) + " " + value_ ) \ {
{ \ return description;
} \ }
virtual const char * what() const noexcept \ };
{ \
return value.c_str(); \ template< typename ParentEx, const char * description >
} \ class defineExStr: public ParentEx
virtual ~exName() noexcept {} \ {
}; public:
explicit defineExStr( std::string const & message_ ):
message( fmt::format( "{} {}", description, message_ ) )
{
}
virtual const char * what() const noexcept
{
return message.c_str();
}
private:
std::string message;
};

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

@ -197,9 +197,6 @@ Preferences::Preferences():
hideSingleTab( false ), hideSingleTab( false ),
mruTabOrder( false ), mruTabOrder( false ),
hideMenubar( false ), hideMenubar( false ),
enableTrayIcon( true ),
startToTray( false ),
closeToTray( true ),
autoStart( false ), autoStart( false ),
doubleClickTranslates( true ), doubleClickTranslates( true ),
selectWordBySingleClick( false ), selectWordBySingleClick( false ),
@ -903,10 +900,11 @@ Class load()
c.preferences.hideSingleTab = ( preferences.namedItem( "hideSingleTab" ).toElement().text() == "1" ); c.preferences.hideSingleTab = ( preferences.namedItem( "hideSingleTab" ).toElement().text() == "1" );
c.preferences.mruTabOrder = ( preferences.namedItem( "mruTabOrder" ).toElement().text() == "1" ); c.preferences.mruTabOrder = ( preferences.namedItem( "mruTabOrder" ).toElement().text() == "1" );
c.preferences.hideMenubar = ( preferences.namedItem( "hideMenubar" ).toElement().text() == "1" ); c.preferences.hideMenubar = ( preferences.namedItem( "hideMenubar" ).toElement().text() == "1" );
#ifndef Q_OS_MACOS // // macOS uses the dock menu instead of the tray icon
c.preferences.enableTrayIcon = ( preferences.namedItem( "enableTrayIcon" ).toElement().text() == "1" ); c.preferences.enableTrayIcon = ( preferences.namedItem( "enableTrayIcon" ).toElement().text() == "1" );
c.preferences.startToTray = ( preferences.namedItem( "startToTray" ).toElement().text() == "1" ); c.preferences.startToTray = ( preferences.namedItem( "startToTray" ).toElement().text() == "1" );
c.preferences.closeToTray = ( preferences.namedItem( "closeToTray" ).toElement().text() == "1" ); c.preferences.closeToTray = ( preferences.namedItem( "closeToTray" ).toElement().text() == "1" );
#endif
c.preferences.autoStart = ( preferences.namedItem( "autoStart" ).toElement().text() == "1" ); c.preferences.autoStart = ( preferences.namedItem( "autoStart" ).toElement().text() == "1" );
c.preferences.alwaysOnTop = ( preferences.namedItem( "alwaysOnTop" ).toElement().text() == "1" ); c.preferences.alwaysOnTop = ( preferences.namedItem( "alwaysOnTop" ).toElement().text() == "1" );
c.preferences.searchInDock = ( preferences.namedItem( "searchInDock" ).toElement().text() == "1" ); c.preferences.searchInDock = ( preferences.namedItem( "searchInDock" ).toElement().text() == "1" );

View file

@ -341,9 +341,17 @@ struct Preferences
bool hideSingleTab; bool hideSingleTab;
bool mruTabOrder; bool mruTabOrder;
bool hideMenubar; bool hideMenubar;
bool enableTrayIcon;
bool startToTray; #ifdef Q_OS_MACOS // macOS uses the dock menu instead of the tray icon
bool closeToTray; bool closeToTray = false;
bool enableTrayIcon = false;
bool startToTray = false;
#else
bool enableTrayIcon = true;
bool closeToTray = true;
bool startToTray = false;
#endif
bool autoStart; bool autoStart;
bool doubleClickTranslates; bool doubleClickTranslates;
bool selectWordBySingleClick; bool selectWordBySingleClick;
@ -484,7 +492,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 )
@ -690,7 +698,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
@ -729,13 +737,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;
@ -884,7 +895,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
@ -921,7 +932,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

@ -46,10 +46,6 @@ using BtreeIndexing::IndexInfo;
namespace { namespace {
DEF_EX_STR( exNotAardFile, "Not an AARD file", Dictionary::Ex )
DEF_EX_STR( exWordIsTooLarge, "Enountered a word that is too large:", Dictionary::Ex )
DEF_EX_STR( exSuddenEndOfFile, "Sudden end of file", Dictionary::Ex )
#pragma pack( push, 1 ) #pragma pack( push, 1 )
/// AAR file header /// AAR file header

View file

@ -169,10 +169,6 @@ void addEntryToIndex( string & word,
indexedWords.addWord( Utf8::decode( word ), articleOffset ); indexedWords.addWord( Utf8::decode( word ), articleOffset );
} }
DEF_EX( exFailedToDecompressArticle, "Failed to decompress article's body", Dictionary::Ex )
DEF_EX( exChunkIndexOutOfRange, "Chunk index is out of range", Dictionary::Ex )
class BglDictionary: public BtreeIndexing::BtreeDictionary class BglDictionary: public BtreeIndexing::BtreeDictionary
{ {
QMutex idxMutex; QMutex idxMutex;

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>
@ -301,7 +298,6 @@ namespace {
////////////////// GLS Dictionary ////////////////// GLS Dictionary
using Dictionary::exCantReadFile; using Dictionary::exCantReadFile;
DEF_EX( exUserAbort, "User abort", Dictionary::Ex )
DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex ) DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex )
enum { enum {

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

@ -224,7 +224,7 @@ void loadDictionaries( QWidget * parent,
loadDicts.wait(); loadDicts.wait();
if ( loadDicts.getExceptionText().size() ) { if ( !loadDicts.getExceptionText().empty() ) {
QMessageBox::critical( parent, QMessageBox::critical( parent,
QCoreApplication::translate( "LoadDictionaries", "Error loading dictionaries" ), QCoreApplication::translate( "LoadDictionaries", "Error loading dictionaries" ),
QString::fromUtf8( loadDicts.getExceptionText().c_str() ) ); QString::fromUtf8( loadDicts.getExceptionText().c_str() ) );

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

@ -64,14 +64,11 @@ DEF_EX( exNotAnIfoFile, "Not an .ifo file", Dictionary::Ex )
DEF_EX_STR( exBadFieldInIfo, "Bad field in .ifo file encountered:", Dictionary::Ex ) DEF_EX_STR( exBadFieldInIfo, "Bad field in .ifo file encountered:", Dictionary::Ex )
DEF_EX_STR( exNoIdxFile, "No corresponding .idx file was found for", Dictionary::Ex ) DEF_EX_STR( exNoIdxFile, "No corresponding .idx file was found for", Dictionary::Ex )
DEF_EX_STR( exNoDictFile, "No corresponding .dict file was found for", Dictionary::Ex ) DEF_EX_STR( exNoDictFile, "No corresponding .dict file was found for", Dictionary::Ex )
DEF_EX_STR( exNoSynFile, "No corresponding .syn file was found for", Dictionary::Ex )
DEF_EX( ex64BitsNotSupported, "64-bit indices are not presently supported, sorry", Dictionary::Ex ) DEF_EX( ex64BitsNotSupported, "64-bit indices are not presently supported, sorry", Dictionary::Ex )
DEF_EX( exDicttypeNotSupported, "Dictionaries with dicttypes are not supported, sorry", Dictionary::Ex ) DEF_EX( exDicttypeNotSupported, "Dictionaries with dicttypes are not supported, sorry", Dictionary::Ex )
using Dictionary::exCantReadFile; using Dictionary::exCantReadFile;
DEF_EX_STR( exWordIsTooLarge, "Enountered a word that is too large:", Dictionary::Ex )
DEF_EX_STR( exSuddenEndOfFile, "Sudden end of file", Dictionary::Ex )
DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex ) DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex )
DEF_EX_STR( exIncorrectOffset, "Incorrect offset encountered in file", Dictionary::Ex ) DEF_EX_STR( exIncorrectOffset, "Incorrect offset encountered in file", Dictionary::Ex )

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

@ -81,7 +81,6 @@ namespace {
using Dictionary::exCantReadFile; using Dictionary::exCantReadFile;
DEF_EX_STR( exNotXdxfFile, "The file is not an XDXF file:", Dictionary::Ex ) DEF_EX_STR( exNotXdxfFile, "The file is not an XDXF file:", Dictionary::Ex )
DEF_EX( exCorruptedIndex, "The index file is corrupted", Dictionary::Ex )
DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex ) DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex )
enum { enum {
@ -904,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;
@ -1227,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 );
@ -1247,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>
@ -30,23 +37,24 @@
#endif #endif
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
#define __SECURITYHI__ #import <Carbon/Carbon.h>
#include <Carbon/Carbon.h>
#endif #endif
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
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
}; };
@ -99,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 );
@ -118,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()
@ -206,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

@ -2,11 +2,13 @@
* 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 "hotkeywrapper.hh" #include "hotkeywrapper.hh"
#include <QTimer>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
#include <QObject> #include <QObject>
#include <QPushButton>
#include <QTimer>
#include <memory> #include <memory>
#include <vector>
#import <Appkit/Appkit.h> #import <Appkit/Appkit.h>
@ -23,120 +25,107 @@
/// https://github.com/sindresorhus/KeyboardShortcuts/blob/9369a045a72a5296150879781321aecd228171db/readme.md?plain=1#L207 /// https://github.com/sindresorhus/KeyboardShortcuts/blob/9369a045a72a5296150879781321aecd228171db/readme.md?plain=1#L207
/// ///
namespace MacKeyMapping namespace MacKeyMapping {
{
// Convert Qt key codes to Mac OS X native codes // Convert Qt key codes to Mac OS X native codes
struct ReverseMapEntry struct ReverseMapEntry {
{ UniChar character;
UniChar character; UInt16 keyCode;
UInt16 keyCode;
}; };
static struct ReverseMapEntry * mapping; static std::vector<ReverseMapEntry> mapping;
static int mapEntries = 0;
/// References:
/// * https://github.com/libsdl-org/SDL/blob/fc12cc6dfd859a4e01376162a58f12208e539ac6/src/video/cocoa/SDL_cocoakeyboard.m#L345
/// * https://github.com/qt/qtbase/blob/922369844fcb75386237bca3eef59edd5093f58d/src/gui/platform/darwin/qapplekeymapper.mm#L449
///
/// Known possible flaws 1) UCKeyTranslate doesn't handle modifiers at all 2) Handling keyboard switching
void createMapping() void createMapping()
{ {
if( mapping == NULL ) if (mapping.empty()) {
{ mapping.reserve(128);
TISInputSourceRef inputSourceRef = TISCopyInputSourceForLanguage( CFSTR( "en" ) );
if ( !inputSourceRef ) {
inputSourceRef = TISCopyCurrentKeyboardInputSource();
}
if ( !inputSourceRef ) {
return;
}
CFDataRef dataRef = ( CFDataRef )TISGetInputSourceProperty( inputSourceRef, TISInputSourceRef inputSourceRef = TISCopyCurrentKeyboardLayoutInputSource();
kTISPropertyUnicodeKeyLayoutData ); if (!inputSourceRef) {
// this method returns null under macos Japanese input method(and also Chinese), which causes cmd+C+C not to be registered as a hotkey return;
if( !dataRef )
{
// solve the null value under Japanese keyboard
inputSourceRef = TISCopyCurrentKeyboardLayoutInputSource();
dataRef = static_cast<CFDataRef>((TISGetInputSourceProperty(inputSourceRef, kTISPropertyUnicodeKeyLayoutData)));
if (!dataRef) {
return;
} }
}
const UCKeyboardLayout * keyboardLayoutPtr = ( const UCKeyboardLayout * )CFDataGetBytePtr( dataRef ); CFDataRef uchrDataRef = (CFDataRef)TISGetInputSourceProperty(inputSourceRef, kTISPropertyUnicodeKeyLayoutData);
if( !keyboardLayoutPtr ) {
return;
}
mapping = ( struct ReverseMapEntry * )calloc( 128 , sizeof(struct ReverseMapEntry) ); const UCKeyboardLayout* UCKeyboardLayoutPtr;
if( !mapping ) {
return;
}
mapEntries = 0; if (uchrDataRef) {
UCKeyboardLayoutPtr = (const UCKeyboardLayout*)CFDataGetBytePtr(uchrDataRef);
for( int i = 0; i < 128; i++ )
{
UInt32 theDeadKeyState = 0;
UniCharCount theLength = 0;
if( UCKeyTranslate( keyboardLayoutPtr, i, kUCKeyActionDisplay, 0, LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit, &theDeadKeyState, 1, &theLength,
&mapping[ mapEntries ].character ) == noErr && theLength > 0 )
{
if( isprint( mapping[ mapEntries ].character ) )
{
mapping[ mapEntries++ ].keyCode = i;
} }
}
if (!UCKeyboardLayoutPtr) {
return;
}
for (UInt16 i = 0; i < 128; i++) {
UInt32 theDeadKeyState = 0;
UniCharCount theLength = 0;
UniChar temp_char_buf;
if (UCKeyTranslate(UCKeyboardLayoutPtr, i, kUCKeyActionDown, 0, LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit, &theDeadKeyState, 1, &theLength,
&temp_char_buf)
== noErr
&& theLength > 0) {
if (isprint(temp_char_buf)) {
mapping.emplace_back(ReverseMapEntry { temp_char_buf, i });
}
}
}
mapping.shrink_to_fit();
} }
}
} }
quint32 qtKeyToNativeKey( quint32 key ) quint32 qtKeyToNativeKey(UniChar key)
{ {
createMapping(); createMapping();
if( mapping == NULL ) { if (mapping.empty()) {
return 0;
}
for (auto& m : mapping) {
if (m.character == key) {
return m.keyCode;
}
}
return 0; return 0;
} }
for( int i = 0; i < mapEntries; i++ )
{
if( mapping[ i ].character == key ) {
return mapping[ i ].keyCode;
}
}
return 0;
}
} // namespace MacKeyMapping } // namespace MacKeyMapping
static pascal OSStatus hotKeyHandler( EventHandlerCallRef /* nextHandler */, EventRef theEvent, void * userData ) static pascal OSStatus hotKeyHandler(EventHandlerCallRef /* nextHandler */, EventRef theEvent, void* userData)
{ {
EventHotKeyID hkID; EventHotKeyID hkID;
GetEventParameter( theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &hkID ); GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &hkID);
static_cast< HotkeyWrapper * >( userData )->activated( hkID.id ); static_cast<HotkeyWrapper*>(userData)->activated(hkID.id);
return noErr; return noErr;
} }
HotkeyWrapper::HotkeyWrapper( QObject *parent ) HotkeyWrapper::HotkeyWrapper(QObject* parent)
{ {
(void) parent; (void)parent;
hotKeyFunction = NewEventHandlerUPP( hotKeyHandler ); hotKeyFunction = NewEventHandlerUPP(hotKeyHandler);
EventTypeSpec type; EventTypeSpec type;
type.eventClass = kEventClassKeyboard; type.eventClass = kEventClassKeyboard;
type.eventKind = kEventHotKeyPressed; type.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler( hotKeyFunction, 1, &type, this, &handlerRef ); InstallApplicationEventHandler(hotKeyFunction, 1, &type, this, &handlerRef);
keyC = nativeKey( 'c' ); keyC = nativeKey('c');
} }
HotkeyWrapper::~HotkeyWrapper() HotkeyWrapper::~HotkeyWrapper()
{ {
unregister(); unregister();
RemoveEventHandler( handlerRef ); RemoveEventHandler(handlerRef);
} }
void HotkeyWrapper::waitKey2() void HotkeyWrapper::waitKey2()
{ {
state2 = false; state2 = false;
} }
void checkAndRequestAccessibilityPermission() void checkAndRequestAccessibilityPermission()
{ {
@ -159,196 +148,220 @@ void checkAndRequestAccessibilityPermission()
} }
} }
void HotkeyWrapper::activated( int hkId ) void HotkeyWrapper::activated(int hkId)
{ {
if ( state2 ) if (state2) { // wait for 2nd key
{ // wait for 2nd key
waitKey2(); // Cancel the 2nd-key wait stage waitKey2(); // Cancel the 2nd-key wait stage
if ( hkId == state2waiter.id + 1 || if (hkId == state2waiter.id + 1 || (hkId == state2waiter.id && state2waiter.key == state2waiter.key2)) {
( hkId == state2waiter.id && state2waiter.key == state2waiter.key2 ) ) emit hotkeyActivated(state2waiter.handle);
{ return;
emit hotkeyActivated( state2waiter.handle ); }
return;
} }
}
for ( int i = 0; i < hotkeys.count(); i++ ) for (int i = 0; i < hotkeys.count(); i++) {
{ HotkeyStruct& hs = hotkeys[i];
HotkeyStruct &hs = hotkeys[ i ]; if (hkId == hs.id) {
if( hkId == hs.id ) if (hs.key == keyC && hs.modifier == cmdKey) {
{ checkAndRequestAccessibilityPermission();
if( hs.key == keyC && hs.modifier == cmdKey )
{
checkAndRequestAccessibilityPermission();
// 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.
UnregisterEventHotKey( hs.hkRef );
sendCmdC(); // 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.
UnregisterEventHotKey(hs.hkRef);
EventHotKeyID hotKeyID; sendCmdC();
hotKeyID.signature = 'GDHK';
hotKeyID.id = hs.id;
RegisterEventHotKey( hs.key, hs.modifier, hotKeyID, GetApplicationEventTarget(), 0, &hs.hkRef ); EventHotKeyID hotKeyID;
} hotKeyID.signature = 'GDHK';
hotKeyID.id = hs.id;
if ( hs.key2 == 0 ) { RegisterEventHotKey(hs.key, hs.modifier, hotKeyID, GetApplicationEventTarget(), 0, &hs.hkRef);
emit hotkeyActivated( hs.handle ); }
return;
}
state2 = true; if (hs.key2 == 0) {
state2waiter = hs; emit hotkeyActivated(hs.handle);
QTimer::singleShot( 500, this, SLOT( waitKey2() ) ); return;
return; }
state2 = true;
state2waiter = hs;
QTimer::singleShot(500, this, SLOT(waitKey2()));
return;
}
} }
}
state2 = false; state2 = false;
return; return;
} }
void HotkeyWrapper::unregister() void HotkeyWrapper::unregister()
{ {
for ( int i = 0; i < hotkeys.count(); i++ ) for (int i = 0; i < hotkeys.count(); i++) {
{ HotkeyStruct const& hk = hotkeys.at(i);
HotkeyStruct const & hk = hotkeys.at( i );
UnregisterEventHotKey( hk.hkRef ); UnregisterEventHotKey(hk.hkRef);
if ( hk.key2 && hk.key2 != hk.key ) { if (hk.key2 && hk.key2 != hk.key) {
UnregisterEventHotKey( hk.hkRef2 ); UnregisterEventHotKey(hk.hkRef2);
} }
} }
(static_cast< QHotkeyApplication * >( qApp ))->unregisterWrapper( this ); (static_cast<QHotkeyApplication*>(qApp))->unregisterWrapper(this);
} }
bool HotkeyWrapper::setGlobalKey( QKeySequence const & seq, int handle ) bool HotkeyWrapper::setGlobalKey(QKeySequence const& seq, int handle)
{ {
Config::HotKey hk(seq); Config::HotKey hk(seq);
return setGlobalKey(hk.key1,hk.key2,hk.modifiers,handle); return setGlobalKey(hk.key1, hk.key2, hk.modifiers, handle);
} }
bool HotkeyWrapper::setGlobalKey( int key, int key2, Qt::KeyboardModifiers modifier, int handle ) bool HotkeyWrapper::setGlobalKey(int key, int key2, Qt::KeyboardModifiers modifier, int handle)
{ {
if ( !key ) { if (!key) {
return false; // We don't monitor empty combinations return false; // We don't monitor empty combinations
}
quint32 vk = nativeKey(key);
if (vk == 0) {
return false;
}
quint32 vk2 = key2 ? nativeKey(key2) : 0;
static int nextId = 1;
if (nextId > 0xBFFF - 1) {
nextId = 1;
}
quint32 mod = 0;
if (modifier & Qt::CTRL) {
mod |= cmdKey;
}
if (modifier & Qt::ALT) {
mod |= optionKey;
}
if (modifier & Qt::SHIFT) {
mod |= shiftKey;
}
if (modifier & Qt::META) {
mod |= controlKey;
}
hotkeys.append(HotkeyStruct(vk, vk2, mod, handle, nextId));
HotkeyStruct& hk = hotkeys.last();
EventHotKeyID hotKeyID;
hotKeyID.signature = 'GDHK';
hotKeyID.id = nextId;
OSStatus ret = RegisterEventHotKey(vk, mod, hotKeyID, GetApplicationEventTarget(), 0, &hk.hkRef);
if (ret != 0) {
return false;
}
if (vk2 && vk2 != vk) {
hotKeyID.id = nextId + 1;
ret = RegisterEventHotKey(vk2, mod, hotKeyID, GetApplicationEventTarget(), 0, &hk.hkRef2);
}
nextId += 2;
return ret == 0;
} }
quint32 vk = nativeKey( key ); quint32 HotkeyWrapper::nativeKey(int key)
if( vk == 0 ) {
return false;
}
quint32 vk2 = key2 ? nativeKey( key2 ) : 0;
static int nextId = 1;
if( nextId > 0xBFFF - 1 ) {
nextId = 1;
}
quint32 mod = 0;
if( modifier & Qt::CTRL ) {
mod |= cmdKey;
}
if( modifier & Qt::ALT ) {
mod |= optionKey;
}
if( modifier & Qt::SHIFT ) {
mod |= shiftKey;
}
if( modifier & Qt::META ) {
mod |= controlKey;
}
hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, nextId ) );
HotkeyStruct &hk = hotkeys.last();
EventHotKeyID hotKeyID;
hotKeyID.signature = 'GDHK';
hotKeyID.id = nextId;
OSStatus ret = RegisterEventHotKey( vk, mod, hotKeyID, GetApplicationEventTarget(), 0, &hk.hkRef );
if ( ret != 0 ) {
return false;
}
if ( vk2 && vk2 != vk )
{
hotKeyID.id = nextId + 1;
ret = RegisterEventHotKey( vk2, mod, hotKeyID, GetApplicationEventTarget(), 0, &hk.hkRef2 );
}
nextId += 2;
return ret == 0;
}
quint32 HotkeyWrapper::nativeKey( int key )
{ {
switch( key ) { switch (key) {
case Qt::Key_Escape: return 0x35; case Qt::Key_Escape:
case Qt::Key_Tab: return 0x30; return 0x35;
case Qt::Key_Backspace: return 0x33; case Qt::Key_Tab:
case Qt::Key_Return: return 0x24; return 0x30;
case Qt::Key_Enter: return 0x4c; case Qt::Key_Backspace:
case Qt::Key_Delete: return 0x75; return 0x33;
case Qt::Key_Clear: return 0x47; case Qt::Key_Return:
case Qt::Key_Home: return 0x73; return 0x24;
case Qt::Key_End: return 0x77; case Qt::Key_Enter:
case Qt::Key_Left: return 0x7b; return 0x4c;
case Qt::Key_Up: return 0x7e; case Qt::Key_Delete:
case Qt::Key_Right: return 0x7c; return 0x75;
case Qt::Key_Down: return 0x7d; case Qt::Key_Clear:
case Qt::Key_PageUp: return 0x74; return 0x47;
case Qt::Key_PageDown: return 0x79; case Qt::Key_Home:
case Qt::Key_CapsLock: return 0x57; return 0x73;
case Qt::Key_F1: return 0x7a; case Qt::Key_End:
case Qt::Key_F2: return 0x78; return 0x77;
case Qt::Key_F3: return 0x63; case Qt::Key_Left:
case Qt::Key_F4: return 0x76; return 0x7b;
case Qt::Key_F5: return 0x60; case Qt::Key_Up:
case Qt::Key_F6: return 0x61; return 0x7e;
case Qt::Key_F7: return 0x62; case Qt::Key_Right:
case Qt::Key_F8: return 0x64; return 0x7c;
case Qt::Key_F9: return 0x65; case Qt::Key_Down:
case Qt::Key_F10: return 0x6d; return 0x7d;
case Qt::Key_F11: return 0x67; case Qt::Key_PageUp:
case Qt::Key_F12: return 0x6f; return 0x74;
case Qt::Key_F13: return 0x69; case Qt::Key_PageDown:
case Qt::Key_F14: return 0x6b; return 0x79;
case Qt::Key_F15: return 0x71; case Qt::Key_CapsLock:
case Qt::Key_Help: return 0x72; return 0x57;
case Qt::Key_F1:
return 0x7a;
case Qt::Key_F2:
return 0x78;
case Qt::Key_F3:
return 0x63;
case Qt::Key_F4:
return 0x76;
case Qt::Key_F5:
return 0x60;
case Qt::Key_F6:
return 0x61;
case Qt::Key_F7:
return 0x62;
case Qt::Key_F8:
return 0x64;
case Qt::Key_F9:
return 0x65;
case Qt::Key_F10:
return 0x6d;
case Qt::Key_F11:
return 0x67;
case Qt::Key_F12:
return 0x6f;
case Qt::Key_F13:
return 0x69;
case Qt::Key_F14:
return 0x6b;
case Qt::Key_F15:
return 0x71;
case Qt::Key_Help:
return 0x72;
default:; default:;
} }
return MacKeyMapping::qtKeyToNativeKey( QChar( key ).toLower().toLatin1() ); return MacKeyMapping::qtKeyToNativeKey(QChar(key).toLower().unicode());
} }
void HotkeyWrapper::sendCmdC() void HotkeyWrapper::sendCmdC()
{ {
CGEventFlags flags = kCGEventFlagMaskCommand; CGEventFlags flags = kCGEventFlagMaskCommand;
CGEventRef ev; CGEventRef ev;
CGEventSourceRef source = CGEventSourceCreate( kCGEventSourceStateCombinedSessionState ); CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
//press down // press down
ev = CGEventCreateKeyboardEvent( source, keyC, true ); ev = CGEventCreateKeyboardEvent(source, keyC, true);
CGEventSetFlags( ev, CGEventFlags( flags | CGEventGetFlags( ev ) ) ); //combine flags CGEventSetFlags(ev, CGEventFlags(flags | CGEventGetFlags(ev))); // combine flags
CGEventPost( kCGAnnotatedSessionEventTap, ev ); CGEventPost(kCGAnnotatedSessionEventTap, ev);
CFRelease( ev ); CFRelease(ev);
//press up // press up
ev = CGEventCreateKeyboardEvent( source, keyC, false ); ev = CGEventCreateKeyboardEvent(source, keyC, false);
CGEventSetFlags( ev, CGEventFlags( flags | CGEventGetFlags( ev ) ) ); //combine flags CGEventSetFlags(ev, CGEventFlags(flags | CGEventGetFlags(ev))); // combine flags
CGEventPost( kCGAnnotatedSessionEventTap, ev ); CGEventPost(kCGAnnotatedSessionEventTap, ev);
CFRelease( ev ); CFRelease(ev);
CFRelease( source ); CFRelease(source);
} }
EventHandlerUPP HotkeyWrapper::hotKeyFunction = NULL; EventHandlerUPP HotkeyWrapper::hotKeyFunction = NULL;

View file

@ -1,372 +1,344 @@
#include "macmouseover.hh" #include "macmouseover.hh"
#include <AppKit/NSTouch.h> #import <AppKit/AppKit.h>
#include <AppKit/NSEvent.h>
#include <AppKit/NSScreen.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/Foundation.h>
#ifndef AVAILABLE_MAC_OS_X_VERSION_10_11_AND_LATER
#define kAXValueTypeCGPoint kAXValueCGPointType
#define kAXValueTypeCFRange kAXValueCFRangeType
#endif
const int mouseOverInterval = 300; const int mouseOverInterval = 300;
CGEventRef eventCallback( CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon ) CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon)
{ {
(void) proxy; (void)proxy;
if( type != kCGEventMouseMoved ) { if (type != kCGEventMouseMoved) {
return event;
}
static_cast<MacMouseOver*>(refcon)->mouseMoved();
return event; return event;
} }
static_cast< MacMouseOver * >( refcon )->mouseMoved();
return event;
}
static CGPoint carbonScreenPointFromCocoaScreenPoint( NSPoint cocoaPoint ) static CGPoint carbonScreenPointFromCocoaScreenPoint(NSPoint cocoaPoint)
{ {
NSScreen *foundScreen = nil; NSScreen* foundScreen = nil;
CGPoint thePoint; CGPoint thePoint;
for (NSScreen *screen in [NSScreen screens]) { for (NSScreen* screen in [NSScreen screens]) {
if (NSPointInRect(cocoaPoint, [screen frame])) { if (NSPointInRect(cocoaPoint, [screen frame])) {
foundScreen = screen; foundScreen = screen;
}
} }
}
if (foundScreen) { if (foundScreen) {
CGFloat screenHeight = [foundScreen frame].size.height; CGFloat screenHeight = [foundScreen frame].size.height;
thePoint = CGPointMake(cocoaPoint.x, screenHeight - cocoaPoint.y - 1); thePoint = CGPointMake(cocoaPoint.x, screenHeight - cocoaPoint.y - 1);
} } else {
else { thePoint = CGPointMake(0.0, 0.0);
thePoint = CGPointMake(0.0, 0.0); }
return thePoint;
} }
return thePoint; MacMouseOver& MacMouseOver::instance()
}
MacMouseOver & MacMouseOver::instance()
{ {
static MacMouseOver m; static MacMouseOver m;
return m; return m;
} }
MacMouseOver::MacMouseOver() : MacMouseOver::MacMouseOver()
pPref(NULL) : pPref(NULL)
, tapRef( 0 ) , tapRef(0)
, loop( 0 ) , loop(0)
{ {
mouseTimer.setSingleShot( true ); mouseTimer.setSingleShot(true);
connect( &mouseTimer, SIGNAL( timeout() ), this, SLOT( timerShot() ) ); connect(&mouseTimer, SIGNAL(timeout()), this, SLOT(timerShot()));
elementSystemWide = AXUIElementCreateSystemWide(); elementSystemWide = AXUIElementCreateSystemWide();
} }
MacMouseOver::~MacMouseOver() MacMouseOver::~MacMouseOver()
{ {
disableMouseOver(); disableMouseOver();
if( tapRef ) { if (tapRef) {
CFRelease( tapRef ); CFRelease(tapRef);
}
if (loop) {
CFRelease(loop);
}
if (elementSystemWide) {
CFRelease(elementSystemWide);
}
} }
if( loop ) { QString MacMouseOver::CFStringRefToQString(CFStringRef str)
CFRelease( loop );
}
if( elementSystemWide ) {
CFRelease( elementSystemWide );
}
}
QString MacMouseOver::CFStringRefToQString( CFStringRef str )
{ {
int length = CFStringGetLength( str ); int length = CFStringGetLength(str);
if( length == 0 ) { if (length == 0) {
return QString(); return QString();
} }
UniChar *chars = new UniChar[ length ]; UniChar* chars = new UniChar[length];
CFStringGetCharacters( str, CFRangeMake( 0, length ), chars ); CFStringGetCharacters(str, CFRangeMake(0, length), chars);
QString result = QString::fromUtf16( (char16_t*)chars, length );
delete[] chars; QString result = QString::fromUtf16((char16_t*)chars, length);
return result;
delete[] chars;
return result;
} }
void MacMouseOver::mouseMoved() void MacMouseOver::mouseMoved()
{ {
mouseTimer.start( mouseOverInterval ); mouseTimer.start(mouseOverInterval);
} }
void MacMouseOver::enableMouseOver() void MacMouseOver::enableMouseOver()
{ {
mouseTimer.stop(); mouseTimer.stop();
if( !isAXAPIEnabled() ) { if (!isAXAPIEnabled()) {
return; return;
} }
if( !tapRef ) { if (!tapRef) {
tapRef = CGEventTapCreate( kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, tapRef = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap,
kCGEventTapOptionListenOnly, kCGEventTapOptionListenOnly,
CGEventMaskBit( kCGEventMouseMoved ), CGEventMaskBit(kCGEventMouseMoved),
eventCallback, this ); eventCallback, this);
} }
if( !tapRef ) { if (!tapRef) {
return; return;
} }
if( !loop ) { if (!loop) {
loop = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, tapRef, 0 ); loop = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tapRef, 0);
} }
if( loop ) { if (loop) {
CFRunLoopAddSource( CFRunLoopGetMain(), loop, kCFRunLoopCommonModes ); CFRunLoopAddSource(CFRunLoopGetMain(), loop, kCFRunLoopCommonModes);
} }
} }
void MacMouseOver::disableMouseOver() void MacMouseOver::disableMouseOver()
{ {
mouseTimer.stop(); mouseTimer.stop();
if( loop ) { if (loop) {
CFRunLoopRemoveSource( CFRunLoopGetMain(), loop, kCFRunLoopCommonModes ); CFRunLoopRemoveSource(CFRunLoopGetMain(), loop, kCFRunLoopCommonModes);
} }
} }
void MacMouseOver::timerShot() void MacMouseOver::timerShot()
{ {
if( mouseMutex.tryLock( 0 ) ) { if (mouseMutex.tryLock(0)) {
mouseMutex.unlock(); mouseMutex.unlock();
} else { } else {
return; return;
} }
if( !pPref ) { if (!pPref) {
return; return;
} }
if( !pPref->enableScanPopupModifiers || checkModifiersPressed( pPref->scanPopupModifiers ) ) { if (!pPref->enableScanPopupModifiers || checkModifiersPressed(pPref->scanPopupModifiers)) {
handlePosition(); handlePosition();
} }
} }
void MacMouseOver::handlePosition() void MacMouseOver::handlePosition()
{ {
QMutexLocker _( &mouseMutex ); QMutexLocker _(&mouseMutex);
QString strToTranslate; QString strToTranslate;
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
CGPoint pt = carbonScreenPointFromCocoaScreenPoint( [NSEvent mouseLocation] ); CGPoint pt = carbonScreenPointFromCocoaScreenPoint([NSEvent mouseLocation]);
[ pool drain ]; [pool drain];
CFArrayRef names = 0; CFArrayRef names = 0;
AXUIElementRef elem = 0; AXUIElementRef elem = 0;
AXError err = AXUIElementCopyElementAtPosition( elementSystemWide, pt.x, pt.y, &elem ); AXError err = AXUIElementCopyElementAtPosition(elementSystemWide, pt.x, pt.y, &elem);
if( err != kAXErrorSuccess ) { if (err != kAXErrorSuccess) {
return; return;
} }
for( ; ; ) for (;;) {
{ CFTypeRef parameter = AXValueCreate(kAXValueTypeCGPoint, &pt);
CFTypeRef parameter = AXValueCreate( kAXValueTypeCGPoint, &pt ); CFTypeRef rangeValue;
CFTypeRef rangeValue; err = AXUIElementCopyParameterizedAttributeNames(elem, &names);
err = AXUIElementCopyParameterizedAttributeNames( elem, &names ); if (err != kAXErrorSuccess) {
if( err != kAXErrorSuccess ) {
break;
}
int numOfAttributes = CFArrayGetCount( names );
if( CFArrayContainsValue( names, CFRangeMake( 0, numOfAttributes ), CFSTR( "AXRangeForPosition" ) ) )
{
// Standard interface
err = AXUIElementCopyParameterizedAttributeValue( elem, kAXRangeForPositionParameterizedAttribute,
parameter, ( CFTypeRef * )&rangeValue );
CFRelease( parameter );
if( err != kAXErrorSuccess ) {
break;
}
CFStringRef stringValue;
CFRange decodedRange = CFRangeMake( 0, 0 );
bool b = AXValueGetValue( (AXValueRef)rangeValue, kAXValueTypeCFRange, &decodedRange );
CFRelease( rangeValue );
if( b )
{
int fromPos = decodedRange.location - 127;
if( fromPos < 0 ) {
fromPos = 0;
}
int wordPos = decodedRange.location - fromPos; // Cursor position in result string
CFRange range = CFRangeMake( fromPos, wordPos + 1 );
parameter = AXValueCreate( kAXValueTypeCFRange, &range );
err = AXUIElementCopyParameterizedAttributeValue( elem, kAXStringForRangeParameterizedAttribute,
parameter, (CFTypeRef *)&stringValue );
CFRelease( parameter );
if( err != kAXErrorSuccess ) {
break;
}
strToTranslate = CFStringRefToQString( stringValue );
CFRelease( stringValue );
// Read string further
for( int i = 1; i < 128; i++ )
{
range = CFRangeMake( decodedRange.location + i, 1 );
parameter = AXValueCreate( kAXValueTypeCFRange, &range );
err = AXUIElementCopyParameterizedAttributeValue( elem, kAXStringForRangeParameterizedAttribute,
parameter, (CFTypeRef *)&stringValue );
CFRelease( parameter );
if( err != kAXErrorSuccess ) {
break; break;
}
QString s = CFStringRefToQString( stringValue );
CFRelease( stringValue );
if( s[ 0 ].isLetterOrNumber() || s[ 0 ] == '-' ) {
strToTranslate += s;
} else {
break;
}
} }
handleRetrievedString( strToTranslate, wordPos ); int numOfAttributes = CFArrayGetCount(names);
} if (CFArrayContainsValue(names, CFRangeMake(0, numOfAttributes), CFSTR("AXRangeForPosition"))) {
} // Standard interface
else if( CFArrayContainsValue( names, CFRangeMake( 0, numOfAttributes ), CFSTR( "AXTextMarkerForPosition" ) ) ) err = AXUIElementCopyParameterizedAttributeValue(elem, kAXRangeForPositionParameterizedAttribute,
{ parameter, (CFTypeRef*)&rangeValue);
// Safari interface CFRelease(parameter);
CFTypeRef marker, range; if (err != kAXErrorSuccess) {
CFStringRef str; break;
err = AXUIElementCopyParameterizedAttributeValue( elem, CFSTR( "AXTextMarkerForPosition" ), }
parameter, ( CFTypeRef * )&marker );
CFRelease( parameter ); CFStringRef stringValue;
if( err != kAXErrorSuccess ) {
CFRange decodedRange = CFRangeMake(0, 0);
bool b = AXValueGetValue((AXValueRef)rangeValue, kAXValueTypeCFRange, &decodedRange);
CFRelease(rangeValue);
if (b) {
int fromPos = decodedRange.location - 127;
if (fromPos < 0) {
fromPos = 0;
}
int wordPos = decodedRange.location - fromPos; // Cursor position in result string
CFRange range = CFRangeMake(fromPos, wordPos + 1);
parameter = AXValueCreate(kAXValueTypeCFRange, &range);
err = AXUIElementCopyParameterizedAttributeValue(elem, kAXStringForRangeParameterizedAttribute,
parameter, (CFTypeRef*)&stringValue);
CFRelease(parameter);
if (err != kAXErrorSuccess) {
break;
}
strToTranslate = CFStringRefToQString(stringValue);
CFRelease(stringValue);
// Read string further
for (int i = 1; i < 128; i++) {
range = CFRangeMake(decodedRange.location + i, 1);
parameter = AXValueCreate(kAXValueTypeCFRange, &range);
err = AXUIElementCopyParameterizedAttributeValue(elem, kAXStringForRangeParameterizedAttribute,
parameter, (CFTypeRef*)&stringValue);
CFRelease(parameter);
if (err != kAXErrorSuccess) {
break;
}
QString s = CFStringRefToQString(stringValue);
CFRelease(stringValue);
if (s[0].isLetterOrNumber() || s[0] == '-') {
strToTranslate += s;
} else {
break;
}
}
handleRetrievedString(strToTranslate, wordPos);
}
} else if (CFArrayContainsValue(names, CFRangeMake(0, numOfAttributes), CFSTR("AXTextMarkerForPosition"))) {
// Safari interface
CFTypeRef marker, range;
CFStringRef str;
err = AXUIElementCopyParameterizedAttributeValue(elem, CFSTR("AXTextMarkerForPosition"),
parameter, (CFTypeRef*)&marker);
CFRelease(parameter);
if (err != kAXErrorSuccess) {
break;
}
err = AXUIElementCopyParameterizedAttributeValue(elem, CFSTR("AXLeftWordTextMarkerRangeForTextMarker"),
marker, (CFTypeRef*)&range);
CFRelease(marker);
if (err != kAXErrorSuccess) {
break;
}
err = AXUIElementCopyParameterizedAttributeValue(elem, CFSTR("AXStringForTextMarkerRange"),
range, (CFTypeRef*)&str);
CFRelease(range);
if (err == kAXErrorSuccess) {
strToTranslate = CFStringRefToQString(str);
CFRelease(str);
handleRetrievedString(strToTranslate, 0);
}
}
break; break;
}
err = AXUIElementCopyParameterizedAttributeValue( elem, CFSTR( "AXLeftWordTextMarkerRangeForTextMarker" ),
marker, ( CFTypeRef * )&range );
CFRelease( marker );
if( err != kAXErrorSuccess ) {
break;
}
err = AXUIElementCopyParameterizedAttributeValue( elem, CFSTR( "AXStringForTextMarkerRange" ),
range, ( CFTypeRef * )&str );
CFRelease( range );
if( err == kAXErrorSuccess )
{
strToTranslate = CFStringRefToQString( str );
CFRelease( str );
handleRetrievedString( strToTranslate, 0 );
}
} }
break; if (elem) {
} CFRelease(elem);
if( elem ) { }
CFRelease( elem ); if (names) {
} CFRelease(names);
if( names ) { }
CFRelease( names );
}
} }
void MacMouseOver::handleRetrievedString( QString & wordSeq, int wordSeqPos ) void MacMouseOver::handleRetrievedString(QString& wordSeq, int wordSeqPos)
{ {
if( wordSeq.isEmpty() ) { if (wordSeq.isEmpty()) {
return; return;
}
// locate the word inside the sequence
QString word;
if ( wordSeq[ wordSeqPos ].isSpace() )
{
// Currently we ignore such cases
return;
}
else
if ( !wordSeq[ wordSeqPos ].isLetterOrNumber() )
{
// Special case: the cursor points to something which doesn't look like a
// middle of the word -- assume that it's something that joins two words
// together.
int begin = wordSeqPos;
for( ; begin; --begin ) {
if ( !wordSeq[ begin - 1 ].isLetterOrNumber() ) {
break;
}
}
int end = wordSeqPos;
while( ++end < wordSeq.size() ) {
if ( !wordSeq[ end ].isLetterOrNumber() ) {
break;
}
}
if ( end - begin == 1 )
{
// Well, turns out it was just a single non-letter char, discard it
return;
} }
word = wordSeq.mid( begin, end - begin ); // locate the word inside the sequence
}
else
{
// Cursor points to a letter -- cut the word it points to
int begin = wordSeqPos; QString word;
for( ; begin; --begin ) { if (wordSeq[wordSeqPos].isSpace()) {
if ( !wordSeq[ begin - 1 ].isLetterOrNumber() ) { // Currently we ignore such cases
break; return;
} } else if (!wordSeq[wordSeqPos].isLetterOrNumber()) {
} // Special case: the cursor points to something which doesn't look like a
// middle of the word -- assume that it's something that joins two words
// together.
int end = wordSeqPos; int begin = wordSeqPos;
while( ++end < wordSeq.size() ) for (; begin; --begin) {
{ if (!wordSeq[begin - 1].isLetterOrNumber()) {
if ( !wordSeq[ end ].isLetterOrNumber() ) { break;
break; }
} }
int end = wordSeqPos;
while (++end < wordSeq.size()) {
if (!wordSeq[end].isLetterOrNumber()) {
break;
}
}
if (end - begin == 1) {
// Well, turns out it was just a single non-letter char, discard it
return;
}
word = wordSeq.mid(begin, end - begin);
} else {
// Cursor points to a letter -- cut the word it points to
int begin = wordSeqPos;
for (; begin; --begin) {
if (!wordSeq[begin - 1].isLetterOrNumber()) {
break;
}
}
int end = wordSeqPos;
while (++end < wordSeq.size()) {
if (!wordSeq[end].isLetterOrNumber()) {
break;
}
}
word = wordSeq.mid(begin, end - begin);
} }
word = wordSeq.mid( begin, end - begin );
}
// See if we have an RTL char. Reverse the whole string if we do. // See if we have an RTL char. Reverse the whole string if we do.
for( int x = 0; x < word.size(); ++x ) for (int x = 0; x < word.size(); ++x) {
{ QChar::Direction d = word[x].direction();
QChar::Direction d = word[ x ].direction();
if ( d == QChar::DirR || d == QChar::DirAL || if (d == QChar::DirR || d == QChar::DirAL || d == QChar::DirRLE || d == QChar::DirRLO) {
d == QChar::DirRLE || d == QChar::DirRLO ) std::reverse(word.begin(), word.end());
{ break;
std::reverse( word.begin(), word.end() ); }
break;
} }
}
emit instance().hovered( word, false ); emit instance().hovered(word, false);
} }
bool MacMouseOver::isAXAPIEnabled() bool MacMouseOver::isAXAPIEnabled()
{ {
if( NSFoundationVersionNumber >= 1000 ) { // MacOS 10.9+ if (NSFoundationVersionNumber >= 1000) { // MacOS 10.9+
return AXIsProcessTrusted(); return AXIsProcessTrusted();
} }
return AXAPIEnabled(); return AXAPIEnabled();
} }

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

@ -204,26 +204,11 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
// Those hold pointers to dictionaries, we need to free them. // Those hold pointers to dictionaries, we need to free them.
groupInstances.clear(); groupInstances.clear();
groups.clear();
orderAndProps.clear();
loadDictionaries( this, cfg, dictionaries, dictNetMgr ); loadDictionaries( this, cfg, dictionaries, dictNetMgr );
Instances::updateNames( savedGroups, dictionaries );
Instances::updateNames( savedOrder, dictionaries );
Instances::updateNames( savedInactive, dictionaries );
if ( rebuildGroups ) { if ( rebuildGroups ) {
ui.tabs->removeTab( 1 ); orderAndProps->rebuild( savedOrder, savedInactive, dictionaries );
ui.tabs->removeTab( 1 ); groups->rebuild( dictionaries, savedGroups, orderAndProps->getCurrentDictionaryOrder() );
orderAndProps = new OrderAndProps( this, savedOrder, savedInactive, dictionaries );
groups = new Groups( this, dictionaries, savedGroups, orderAndProps->getCurrentDictionaryOrder() );
ui.tabs->insertTab( 1, orderAndProps, QIcon( ":/icons/book.svg" ), tr( "&Dictionaries" ) );
ui.tabs->insertTab( 2, groups, QIcon( ":/icons/bookcase.svg" ), tr( "&Groups" ) );
connect( groups, &Groups::showDictionaryInfo, this, &EditDictionaries::showDictionaryInfo );
connect( orderAndProps, &OrderAndProps::showDictionaryHeadwords, this, &EditDictionaries::showDictionaryHeadwords );
} }
setUpdatesEnabled( true ); setUpdatesEnabled( true );
} }

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

@ -64,6 +64,24 @@ Groups::Groups( QWidget * parent,
countChanged(); countChanged();
} }
void Groups::rebuild( vector< sptr< Dictionary::Class > > const & dicts_,
Config::Groups const & groups_,
Config::Group const & order )
{
this->setUpdatesEnabled( false );
dicts = dicts_;
groups = groups_;
ui.dictionaries->setAsSource();
ui.dictionaries->populate( Instances::Group( order, dicts, Config::Group() ).dictionaries, dicts );
// Populate groups' widget
ui.groups->populate( groups, dicts, ui.dictionaries->getCurrentDictionaries() );
countChanged();
this->setUpdatesEnabled( true );
}
void Groups::editGroup( unsigned id ) void Groups::editGroup( unsigned id )
{ {
for ( int x = 0; x < groups.size(); ++x ) { for ( int x = 0; x < groups.size(); ++x ) {

View file

@ -18,7 +18,9 @@ public:
std::vector< sptr< Dictionary::Class > > const &, std::vector< sptr< Dictionary::Class > > const &,
Config::Groups const &, Config::Groups const &,
Config::Group const & order ); Config::Group const & order );
void rebuild( std::vector< sptr< Dictionary::Class > > const & dicts_,
Config::Groups const & groups_,
Config::Group const & order );
/// Instructs the dialog to position itself on editing the given group. /// Instructs the dialog to position itself on editing the given group.
void editGroup( unsigned id ); void editGroup( unsigned id );
@ -31,7 +33,7 @@ public:
private: private:
Ui::Groups ui; Ui::Groups ui;
std::vector< sptr< Dictionary::Class > > const & dicts; std::vector< sptr< Dictionary::Class > > dicts;
Config::Groups groups; Config::Groups groups;
QToolButton * groupsListButton; QToolButton * groupsListButton;

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

@ -401,14 +401,18 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
connect( wordsZoomOut, &QAction::triggered, this, &MainWindow::doWordsZoomOut ); connect( wordsZoomOut, &QAction::triggered, this, &MainWindow::doWordsZoomOut );
connect( wordsZoomBase, &QAction::triggered, this, &MainWindow::doWordsZoomBase ); connect( wordsZoomBase, &QAction::triggered, this, &MainWindow::doWordsZoomBase );
// tray icon // tray icon
#ifndef Q_OS_MACOS // macOS uses the dock menu instead of the tray icon
connect( trayIconMenu.addAction( tr( "Show &Main Window" ) ), &QAction::triggered, this, [ this ] { connect( trayIconMenu.addAction( tr( "Show &Main Window" ) ), &QAction::triggered, this, [ this ] {
this->toggleMainWindow( true ); this->toggleMainWindow( true );
} ); } );
#endif
trayIconMenu.addAction( enableScanningAction ); trayIconMenu.addAction( enableScanningAction );
#ifndef Q_OS_MACOS // macOS uses the dock menu instead of the tray icon
trayIconMenu.addSeparator(); trayIconMenu.addSeparator();
connect( trayIconMenu.addAction( tr( "&Quit" ) ), &QAction::triggered, this, &MainWindow::quitApp ); connect( trayIconMenu.addAction( tr( "&Quit" ) ), &QAction::triggered, this, &MainWindow::quitApp );
#endif
addGlobalAction( &escAction, [ this ]() { addGlobalAction( &escAction, [ this ]() {
handleEsc(); handleEsc();
@ -756,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
@ -1420,6 +1420,11 @@ void MainWindow::updateAppearances( QString const & addonStyle,
void MainWindow::trayIconUpdateOrInit() void MainWindow::trayIconUpdateOrInit()
{ {
#ifdef Q_OS_MACOS
trayIconMenu.setAsDockMenu();
ui.actionCloseToTray->setVisible( false );
#else
if ( !cfg.preferences.enableTrayIcon ) { if ( !cfg.preferences.enableTrayIcon ) {
if ( trayIcon ) { if ( trayIcon ) {
delete trayIcon; delete trayIcon;
@ -1443,6 +1448,7 @@ void MainWindow::trayIconUpdateOrInit()
// The 'Close to tray' action is associated with the tray icon, so we hide // The 'Close to tray' action is associated with the tray icon, so we hide
// or show it here. // or show it here.
ui.actionCloseToTray->setVisible( cfg.preferences.enableTrayIcon ); ui.actionCloseToTray->setVisible( cfg.preferences.enableTrayIcon );
#endif
} }
void MainWindow::wheelEvent( QWheelEvent * ev ) void MainWindow::wheelEvent( QWheelEvent * ev )

View file

@ -133,6 +133,25 @@ OrderAndProps::OrderAndProps( QWidget * parent,
showDictNumbers(); showDictNumbers();
} }
void OrderAndProps::rebuild( Config::Group const & dictionaryOrder,
Config::Group const & inactiveDictionaries,
std::vector< sptr< Dictionary::Class > > const & allDictionaries )
{
Instances::Group order( dictionaryOrder, allDictionaries, Config::Group() );
Instances::Group inactive( inactiveDictionaries, allDictionaries, Config::Group() );
Instances::complementDictionaryOrder( order, inactive, allDictionaries );
setUpdatesEnabled( false );
ui.dictionaryOrder->populate( order.dictionaries, allDictionaries );
ui.inactiveDictionaries->populate( inactive.dictionaries, allDictionaries );
disableDictionaryDescription();
showDictNumbers();
setUpdatesEnabled( true );
}
Config::Group OrderAndProps::getCurrentDictionaryOrder() const Config::Group OrderAndProps::getCurrentDictionaryOrder() const
{ {
Instances::Group g; Instances::Group g;

View file

@ -17,7 +17,9 @@ public:
Config::Group const & dictionaryOrder, Config::Group const & dictionaryOrder,
Config::Group const & inactiveDictionaries, Config::Group const & inactiveDictionaries,
std::vector< sptr< Dictionary::Class > > const & allDictionaries ); std::vector< sptr< Dictionary::Class > > const & allDictionaries );
void rebuild( Config::Group const & dictionaryOrder,
Config::Group const & inactiveDictionaries,
std::vector< sptr< Dictionary::Class > > const & allDictionaries );
Config::Group getCurrentDictionaryOrder() const; Config::Group getCurrentDictionaryOrder() const;
Config::Group getCurrentInactiveDictionaries() const; Config::Group getCurrentInactiveDictionaries() const;

View file

@ -174,6 +174,11 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
ui.hideSingleTab->setChecked( p.hideSingleTab ); ui.hideSingleTab->setChecked( p.hideSingleTab );
ui.mruTabOrder->setChecked( p.mruTabOrder ); ui.mruTabOrder->setChecked( p.mruTabOrder );
ui.enableTrayIcon->setChecked( p.enableTrayIcon ); ui.enableTrayIcon->setChecked( p.enableTrayIcon );
#ifdef Q_OS_MACOS // macOS uses the dock menu instead of the tray icon
ui.enableTrayIcon->hide();
#endif
ui.startToTray->setChecked( p.startToTray ); ui.startToTray->setChecked( p.startToTray );
ui.closeToTray->setChecked( p.closeToTray ); ui.closeToTray->setChecked( p.closeToTray );
ui.cbAutostart->setChecked( p.autoStart ); ui.cbAutostart->setChecked( p.autoStart );

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: