Compare commits

...

23 commits

Author SHA1 Message Date
xiaoyifang 677745ba09
Merge 6c63ba45b6 into 8fd5b37335 2024-11-12 01:30:09 -05: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
shenleban tongying 720f66c781 clean: make toggleMainWindow reasonable
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-11 00:18:02 -05:00
shenleban tongying acbfef0870 opt: don't focus on the main window if word comes from headword dialog 2024-11-11 00:18:02 -05:00
shenleban tongying d4db51f278 opt: mdx -> avoid duplicated Adler-32 checksum in zlib decompression 2024-11-10 22:32:22 -05:00
YiFang Xiao 6c63ba45b6 1 2024-11-09 20:40:55 +08:00
YiFang Xiao a9478cdc34 1 2024-11-09 20:17:29 +08:00
xiaoyifang c0697ecdda 1 2024-11-04 11:42:04 +08:00
autofix-ci[bot] 701a4effb3
[autofix.ci] apply automated fixes 2024-11-04 03:29:03 +00:00
xiaoyifang f9a3705942 1 2024-11-04 11:27:46 +08:00
YiFang Xiao 2c91b78e13 1 2024-11-03 17:42:51 +08:00
YiFang Xiao c6387df392 1 2024-11-03 17:42:50 +08:00
autofix-ci[bot] 43c982cf8a
[autofix.ci] apply automated fixes 2024-11-03 07:26:21 +00:00
YiFang Xiao e07e730a09 1 2024-11-03 15:19:32 +08:00
YiFang Xiao ab214cfb05 1 2024-11-03 14:20:10 +08:00
autofix-ci[bot] c00239a1b1 [autofix.ci] apply automated fixes 2024-11-03 14:20:03 +08:00
xiaoyifang 469896bbf1 1 2024-11-03 14:19:19 +08:00
xiaoyifang 0d89c4ab56 1 2024-11-03 14:19:19 +08:00
xiaoyifang 9e99389cc3 opt: website dictionary render html 2024-11-03 14:19:12 +08:00
xiaoyifang 6edfd15962 opt: add option about open website in seperate tab 2024-11-03 14:17:29 +08:00
22 changed files with 380 additions and 253 deletions

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,
@ -26,6 +25,7 @@ Checks: >
-google-default-arguments, -google-default-arguments,
-google-readability-casting, -google-readability-casting,
-hicpp-deprecated-headers, -hicpp-deprecated-headers,
-hicpp-no-array-decay,
-misc-const-correctness, -misc-const-correctness,
-misc-include-cleaner, -misc-include-cleaner,
-misc-non-private-member-variables-in-classes, -misc-non-private-member-variables-in-classes,
@ -43,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

@ -529,6 +529,26 @@ void ArticleRequest::altSearchFinished()
for ( const auto & activeDict : activeDicts ) { for ( const auto & activeDict : activeDicts ) {
try { try {
// if the dictionary is website dictionary and openinNewTab is enabled, emit a signal.
if ( GlobalBroadcaster::instance()->getPreference()->openWebsiteInNewTab ) {
if ( ( activeDict->getFeatures() | Dictionary::WebSite ) == Dictionary::WebSite ) {
//replace the word,and get the actual requested url
string url = activeDict->getProperties()[ Dictionary::Property::Url ];
if ( url.empty() ) {
continue;
}
QString requestUrl = Utils::WebSite::urlReplaceWord( QString::fromStdString( url ), word );
emit GlobalBroadcaster::instance()
-> websiteDictionarySignal( QString::fromStdString( activeDict->getName() ), requestUrl );
QStringList dictIds;
dictIds << QString::fromStdString( activeDict->getId() );
ActiveDictIds hittedWord{ group.id, word, dictIds };
emit GlobalBroadcaster::instance() -> dictionaryChanges( hittedWord );
continue;
}
}
sptr< Dictionary::DataRequest > r = activeDict->getArticle( sptr< Dictionary::DataRequest > r = activeDict->getArticle(
wordStd, wordStd,
altsVector, altsVector,

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

@ -45,4 +45,6 @@ signals:
void dictionaryClear( ActiveDictIds ad ); void dictionaryClear( ActiveDictIds ad );
void indexingDictionary( QString ); void indexingDictionary( QString );
void websiteDictionarySignal( QString, QString );
}; };

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" );
@ -1073,6 +1071,10 @@ Class load()
( preferences.namedItem( "removeInvalidIndexOnExit" ).toElement().text() == "1" ); ( preferences.namedItem( "removeInvalidIndexOnExit" ).toElement().text() == "1" );
} }
if ( !preferences.namedItem( "openWebsiteInNewTab" ).isNull() ) {
c.preferences.openWebsiteInNewTab = ( preferences.namedItem( "openWebsiteInNewTab" ).toElement().text() == "1" );
}
if ( !preferences.namedItem( "maxStringsInHistory" ).isNull() ) { if ( !preferences.namedItem( "maxStringsInHistory" ).isNull() ) {
c.preferences.maxStringsInHistory = preferences.namedItem( "maxStringsInHistory" ).toElement().text().toUInt(); c.preferences.maxStringsInHistory = preferences.namedItem( "maxStringsInHistory" ).toElement().text().toUInt();
} }
@ -2102,6 +2104,10 @@ void save( Class const & c )
opt.appendChild( dd.createTextNode( c.preferences.removeInvalidIndexOnExit ? "1" : "0" ) ); opt.appendChild( dd.createTextNode( c.preferences.removeInvalidIndexOnExit ? "1" : "0" ) );
preferences.appendChild( opt ); preferences.appendChild( opt );
opt = dd.createElement( "openWebsiteInNewTab" );
opt.appendChild( dd.createTextNode( c.preferences.openWebsiteInNewTab ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "maxStringsInHistory" ); opt = dd.createElement( "maxStringsInHistory" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.maxStringsInHistory ) ) ); opt.appendChild( dd.createTextNode( QString::number( c.preferences.maxStringsInHistory ) ) );
preferences.appendChild( opt ); preferences.appendChild( opt );

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;
@ -392,6 +400,7 @@ struct Preferences
int maxNetworkCacheSize; int maxNetworkCacheSize;
bool clearNetworkCacheOnExit; bool clearNetworkCacheOnExit;
bool removeInvalidIndexOnExit = false; bool removeInvalidIndexOnExit = false;
bool openWebsiteInNewTab = false;
qreal zoomFactor; qreal zoomFactor;
qreal helpZoomFactor; qreal helpZoomFactor;

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

@ -34,7 +34,8 @@ enum Property {
Author, Author,
Copyright, Copyright,
Description, Description,
Email Email,
Url,
}; };
DEF_EX( Ex, "Dictionary error", std::exception ) DEF_EX( Ex, "Dictionary error", std::exception )

View file

@ -301,7 +301,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

@ -264,14 +264,12 @@ bool MdictParser::parseCompressedBlock( qint64 compressedBlockSize,
case 0x02000000: case 0x02000000:
// zlib compression // zlib compression
decompressedBlock = zlibDecompress( buf, size ); decompressedBlock = zlibDecompress( buf, size, checksum );
if ( decompressedBlock.isEmpty() ) {
if ( !checkAdler32( decompressedBlock.constData(), decompressedBlock.size(), checksum ) ) { gdWarning( "MDict: parseCompressedBlock: zlib: failed to decompress or checksum does not match" );
gdWarning( "MDict: parseCompressedBlock: zlib: checksum does not match" );
return false; return false;
} }
break; break;
default: default:
gdWarning( "MDict: parseCompressedBlock: unknown type" ); gdWarning( "MDict: parseCompressedBlock: unknown type" );
return false; return false;

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

@ -3,20 +3,21 @@
#include <bzlib.h> #include <bzlib.h>
#include <lzma.h> #include <lzma.h>
#define CHUNK_SIZE 2048 using std::string;
QByteArray zlibDecompress( const char * bufptr, unsigned length ) static constexpr qsizetype CHUNK_SIZE = 2048;
QByteArray zlibDecompress( const char * bufptr, unsigned length, uLong adler32_checksum )
{ {
z_stream zs; z_stream zs{};
char buf[ CHUNK_SIZE ];
QByteArray str; QByteArray str;
int res; int res = Z_OK;
memset( &zs, 0, sizeof( zs ) );
zs.next_in = (Bytef *)bufptr; zs.next_in = (Bytef *)bufptr;
zs.avail_in = length; zs.avail_in = length;
res = inflateInit( &zs ); res = inflateInit( &zs );
if ( res == Z_OK ) { if ( res == Z_OK ) {
char buf[ CHUNK_SIZE ];
while ( res != Z_STREAM_END ) { while ( res != Z_STREAM_END ) {
zs.next_out = (Bytef *)buf; zs.next_out = (Bytef *)buf;
zs.avail_out = CHUNK_SIZE; zs.avail_out = CHUNK_SIZE;
@ -27,9 +28,7 @@ QByteArray zlibDecompress( const char * bufptr, unsigned length )
} }
} }
} }
if ( inflateEnd( &zs ) != Z_OK || res != Z_STREAM_END || ( adler32_checksum != 0 && zs.adler != adler32_checksum ) ) {
inflateEnd( &zs );
if ( res != Z_STREAM_END ) {
str.clear(); str.clear();
} }
return str; return str;
@ -37,7 +36,7 @@ QByteArray zlibDecompress( const char * bufptr, unsigned length )
string decompressZlib( const char * bufptr, unsigned length ) string decompressZlib( const char * bufptr, unsigned length )
{ {
QByteArray b = zlibDecompress( bufptr, length ); QByteArray b = zlibDecompress( bufptr, length, 0 );
return string( b.constData(), b.size() ); return string( b.constData(), b.size() );
} }

View file

@ -3,12 +3,11 @@
#include <QByteArray> #include <QByteArray>
#include <string> #include <string>
using std::string; /// @param adler32_checksum 0 to skip checksum
QByteArray zlibDecompress( const char * bufptr, unsigned length, unsigned long adler32_checksum );
QByteArray zlibDecompress( const char * bufptr, unsigned length ); std::string decompressZlib( const char * bufptr, unsigned length );
string decompressZlib( const char * bufptr, unsigned length ); std::string decompressBzip2( const char * bufptr, unsigned length );
string decompressBzip2( const char * bufptr, unsigned length ); std::string decompressLzma2( const char * bufptr, unsigned length, bool raw_decoder = false );
string decompressLzma2( const char * bufptr, unsigned length, bool raw_decoder = false );

View file

@ -24,7 +24,6 @@ class WebSiteDictionary: public Dictionary::Class
{ {
string name; string name;
QByteArray urlTemplate; QByteArray urlTemplate;
bool experimentalIframe;
QString iconFilename; QString iconFilename;
bool inside_iframe; bool inside_iframe;
QNetworkAccessManager & netMgr; QNetworkAccessManager & netMgr;
@ -41,14 +40,8 @@ public:
name( name_ ), name( name_ ),
iconFilename( iconFilename_ ), iconFilename( iconFilename_ ),
inside_iframe( inside_iframe_ ), inside_iframe( inside_iframe_ ),
netMgr( netMgr_ ), netMgr( netMgr_ )
experimentalIframe( false )
{ {
if ( urlTemplate_.startsWith( "http://" ) || urlTemplate_.startsWith( "https://" ) ) {
experimentalIframe = true;
}
//else file:/// local dictionary file path
urlTemplate = QUrl( urlTemplate_ ).toEncoded(); urlTemplate = QUrl( urlTemplate_ ).toEncoded();
dictionaryDescription = urlTemplate_; dictionaryDescription = urlTemplate_;
} }
@ -60,7 +53,9 @@ public:
map< Property, string > getProperties() noexcept override map< Property, string > getProperties() noexcept override
{ {
return map< Property, string >(); map< Property, string > properties;
properties.insert( { Property::Url, urlTemplate.toStdString() } );
return properties;
} }
unsigned long getArticleCount() noexcept override unsigned long getArticleCount() noexcept override
@ -321,7 +316,7 @@ void WebSiteArticleRequest::requestFinished( QNetworkReply * r )
sptr< DataRequest > WebSiteDictionary::getArticle( wstring const & str, sptr< DataRequest > WebSiteDictionary::getArticle( wstring const & str,
vector< wstring > const & /*alts*/, vector< wstring > const & /*alts*/,
wstring const & context, wstring const & /*context*/,
bool /*ignoreDiacritics*/ ) bool /*ignoreDiacritics*/ )
{ {
QString urlString = Utils::WebSite::urlReplaceWord( QString( urlTemplate ), QString::fromStdU32String( str ) ); QString urlString = Utils::WebSite::urlReplaceWord( QString( urlTemplate ), QString::fromStdU32String( str ) );
@ -335,24 +330,22 @@ sptr< DataRequest > WebSiteDictionary::getArticle( wstring const & str,
QUrl url( urlString ); QUrl url( urlString );
GlobalBroadcaster::instance()->addWhitelist( url.host() ); GlobalBroadcaster::instance()->addWhitelist( url.host() );
QString encodeUrl; const QString & encodeUrl = urlString;
if ( experimentalIframe ) {
encodeUrl = "ifr://localhost?url=" + QUrl::toPercentEncoding( urlString ); if ( GlobalBroadcaster::instance()->getPreference()->openWebsiteInNewTab ) {
result += string( "<div><span>this website dictionary is opened in the new tab</span></div>" );
} }
else { else {
encodeUrl = urlString; fmt::format_to( std::back_inserter( result ),
} R"(<iframe id="gdexpandframe-{}" src="{}"
fmt::format_to( std::back_inserter( result ),
R"(<iframe id="gdexpandframe-{}" src="{}"
onmouseover="processIframeMouseOver('gdexpandframe-{}');" onmouseover="processIframeMouseOver('gdexpandframe-{}');"
onmouseout="processIframeMouseOut();" scrolling="no" onmouseout="processIframeMouseOut();" scrolling="no"
style="overflow:visible; width:100%; display:block; border:none;" style="overflow:visible; width:100%; display:block; border:none;"
sandbox="allow-same-origin allow-scripts allow-popups"></iframe>)", sandbox="allow-same-origin allow-scripts allow-popups"></iframe>)",
getId(), getId(),
encodeUrl.toStdString(), encodeUrl.toStdString(),
getId() ); getId() );
}
auto dr = std::make_shared< DataRequestInstant >( true ); auto dr = std::make_shared< DataRequestInstant >( true );
dr->appendString( result ); dr->appendString( result );
return dr; return dr;

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 {

View file

@ -85,6 +85,162 @@ QString ArticleView::scrollToFromDictionaryId( QString const & dictionaryId )
Q_ASSERT( !isScrollTo( dictionaryId ) ); Q_ASSERT( !isScrollTo( dictionaryId ) );
return scrollToPrefix + dictionaryId; return scrollToPrefix + dictionaryId;
} }
void ArticleView::setupWebview()
{ // setup GUI
this->webview = new ArticleWebView( this );
this->ftsSearchPanel = new FtsSearchPanel( this );
this->searchPanel = new SearchPanel( this );
this->searchPanel->hide();
this->ftsSearchPanel->hide();
auto * baseLayout = new QVBoxLayout( this );
this->tabWidget = new QTabWidget( this );
baseLayout->setContentsMargins( 0, 0, 0, 0 );
baseLayout->addWidget( this->tabWidget );
auto * tab1 = new QWidget( tabWidget );
// Layout
auto * mainLayout = new QVBoxLayout( tab1 );
mainLayout->addWidget( this->webview );
mainLayout->addWidget( this->ftsSearchPanel );
mainLayout->addWidget( this->searchPanel );
this->webview->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
this->ftsSearchPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
this->searchPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
mainLayout->setContentsMargins( 0, 0, 0, 0 );
this->tabWidget->addTab( tab1, "Dictionaries" );
this->tabWidget->setTabBarAutoHide( true );
// end UI setup
connect( this->searchPanel->previous, &QPushButton::clicked, this, &ArticleView::on_searchPrevious_clicked );
connect( this->searchPanel->next, &QPushButton::clicked, this, &ArticleView::on_searchNext_clicked );
connect( this->searchPanel->close, &QPushButton::clicked, this, &ArticleView::on_searchCloseButton_clicked );
connect( this->searchPanel->caseSensitive, &QCheckBox::toggled, this, &ArticleView::on_searchCaseSensitive_clicked );
connect( this->searchPanel->lineEdit, &QLineEdit::textEdited, this, &ArticleView::on_searchText_textEdited );
connect( this->searchPanel->lineEdit, &QLineEdit::returnPressed, this, &ArticleView::on_searchText_returnPressed );
connect( this->ftsSearchPanel->next, &QPushButton::clicked, this, &ArticleView::on_ftsSearchNext_clicked );
connect( this->ftsSearchPanel->previous, &QPushButton::clicked, this, &ArticleView::on_ftsSearchPrevious_clicked );
//
this->webview->setUp( const_cast< Config::Class * >( &this->cfg ) );
this->syncBackgroundColorWithCfgDarkReader();
this->goBackAction.setShortcut( QKeySequence( "Alt+Left" ) );
this->webview->addAction( &this->goBackAction );
connect( &this->goBackAction, &QAction::triggered, this, &ArticleView::back );
this->goForwardAction.setShortcut( QKeySequence( "Alt+Right" ) );
this->webview->addAction( &this->goForwardAction );
connect( &this->goForwardAction, &QAction::triggered, this, &ArticleView::forward );
this->webview->pageAction( QWebEnginePage::Copy )->setShortcut( QKeySequence::Copy );
this->webview->addAction( this->webview->pageAction( QWebEnginePage::Copy ) );
QAction * selectAll = this->webview->pageAction( QWebEnginePage::SelectAll );
selectAll->setShortcut( QKeySequence::SelectAll );
selectAll->setShortcutContext( Qt::WidgetWithChildrenShortcut );
this->webview->addAction( selectAll );
this->webview->setContextMenuPolicy( Qt::CustomContextMenu );
connect( this->webview, &QWebEngineView::loadFinished, this, &ArticleView::loadFinished );
connect( this->webview, &ArticleWebView::linkClicked, this, &ArticleView::linkClicked );
connect( this->webview->page(), &QWebEnginePage::titleChanged, this, &ArticleView::handleTitleChanged );
connect( this->webview, &QWidget::customContextMenuRequested, this, &ArticleView::contextMenuRequested );
connect( this->webview->page(), &QWebEnginePage::linkHovered, this, &ArticleView::linkHovered );
connect( this->webview, &ArticleWebView::doubleClicked, this, &ArticleView::doubleClicked );
this->pasteAction.setShortcut( QKeySequence::Paste );
this->webview->addAction( &this->pasteAction );
connect( &this->pasteAction, &QAction::triggered, this, &ArticleView::pasteTriggered );
this->articleUpAction.setShortcut( QKeySequence( "Alt+Up" ) );
this->webview->addAction( &this->articleUpAction );
connect( &this->articleUpAction, &QAction::triggered, this, &ArticleView::moveOneArticleUp );
this->articleDownAction.setShortcut( QKeySequence( "Alt+Down" ) );
this->webview->addAction( &this->articleDownAction );
connect( &this->articleDownAction, &QAction::triggered, this, &ArticleView::moveOneArticleDown );
this->selectCurrentArticleAction.setShortcut( QKeySequence( "Ctrl+Shift+A" ) );
this->selectCurrentArticleAction.setText( tr( "Select Current Article" ) );
this->webview->addAction( &this->selectCurrentArticleAction );
connect( &this->selectCurrentArticleAction, &QAction::triggered, this, &ArticleView::selectCurrentArticle );
this->copyAsTextAction.setShortcut( QKeySequence( "Ctrl+Shift+C" ) );
this->copyAsTextAction.setText( tr( "Copy as text" ) );
this->webview->addAction( &this->copyAsTextAction );
connect( &this->copyAsTextAction, &QAction::triggered, this, &ArticleView::copyAsText );
this->inspectAction.setShortcut( QKeySequence( Qt::Key_F12 ) );
this->inspectAction.setText( tr( "Inspect" ) );
this->webview->addAction( &this->inspectAction );
connect( &this->inspectAction, &QAction::triggered, this, &ArticleView::inspectElement );
this->webview->installEventFilter( this );
this->searchPanel->installEventFilter( this );
this->ftsSearchPanel->installEventFilter( this );
QWebEngineSettings * settings = this->webview->settings();
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::LocalContentCanAccessFileUrls, true );
settings->setAttribute( QWebEngineSettings::ErrorPageEnabled, false );
settings->setAttribute( QWebEngineSettings::LinksIncludedInFocusChain, false );
settings->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false );
settings->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true );
settings->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false );
#endif
this->expandOptionalParts = this->cfg.preferences.alwaysExpandOptionalParts;
#ifndef Q_OS_MACOS
this->webview->grabGesture( Gestures::GDPinchGestureType );
this->webview->grabGesture( Gestures::GDSwipeGestureType );
#endif
}
void ArticleView::addWebsiteTab( QString name, QString url )
{
auto * view = new QWebEngineView( this );
view->load( url );
view->setZoomFactor( this->cfg.preferences.zoomFactor );
int index = tabWidget->count();
QString escaped = Utils::escapeAmps( name );
tabWidget->insertTab( index, view, escaped );
}
void ArticleView::clearWebsiteTabs()
{
int index = tabWidget->count();
// keep the first tab
while ( index-- > 1 ) {
tabWidget->removeTab( index );
}
}
ArticleView::ArticleView( QWidget * parent, ArticleView::ArticleView( QWidget * parent,
ArticleNetworkAccessManager & nm, ArticleNetworkAccessManager & nm,
@ -115,133 +271,11 @@ ArticleView::ArticleView( QWidget * parent,
translateLine( translateLine_ ) translateLine( translateLine_ )
{ {
// setup GUI // setup GUI
webview = new ArticleWebView( this ); setupWebview();
ftsSearchPanel = new FtsSearchPanel( this );
searchPanel = new SearchPanel( this );
searchPanel->hide();
ftsSearchPanel->hide();
// Layout
auto * mainLayout = new QVBoxLayout( this );
mainLayout->addWidget( webview );
mainLayout->addWidget( ftsSearchPanel );
mainLayout->addWidget( searchPanel );
webview->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); auto html = this->articleNetMgr.getHtml( ResourceType::UNTITLE );
ftsSearchPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
searchPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
mainLayout->setContentsMargins( 0, 0, 0, 0 );
// end UI setup
connect( searchPanel->previous, &QPushButton::clicked, this, &ArticleView::on_searchPrevious_clicked );
connect( searchPanel->next, &QPushButton::clicked, this, &ArticleView::on_searchNext_clicked );
connect( searchPanel->close, &QPushButton::clicked, this, &ArticleView::on_searchCloseButton_clicked );
connect( searchPanel->caseSensitive, &QCheckBox::toggled, this, &ArticleView::on_searchCaseSensitive_clicked );
connect( searchPanel->lineEdit, &QLineEdit::textEdited, this, &ArticleView::on_searchText_textEdited );
connect( searchPanel->lineEdit, &QLineEdit::returnPressed, this, &ArticleView::on_searchText_returnPressed );
connect( ftsSearchPanel->next, &QPushButton::clicked, this, &ArticleView::on_ftsSearchNext_clicked );
connect( ftsSearchPanel->previous, &QPushButton::clicked, this, &ArticleView::on_ftsSearchPrevious_clicked );
//
webview->setUp( const_cast< Config::Class * >( &cfg ) );
syncBackgroundColorWithCfgDarkReader();
goBackAction.setShortcut( QKeySequence( "Alt+Left" ) );
webview->addAction( &goBackAction );
connect( &goBackAction, &QAction::triggered, this, &ArticleView::back );
goForwardAction.setShortcut( QKeySequence( "Alt+Right" ) );
webview->addAction( &goForwardAction );
connect( &goForwardAction, &QAction::triggered, this, &ArticleView::forward );
webview->pageAction( QWebEnginePage::Copy )->setShortcut( QKeySequence::Copy );
webview->addAction( webview->pageAction( QWebEnginePage::Copy ) );
QAction * selectAll = webview->pageAction( QWebEnginePage::SelectAll );
selectAll->setShortcut( QKeySequence::SelectAll );
selectAll->setShortcutContext( Qt::WidgetWithChildrenShortcut );
webview->addAction( selectAll );
webview->setContextMenuPolicy( Qt::CustomContextMenu );
connect( webview, &QWebEngineView::loadFinished, this, &ArticleView::loadFinished );
connect( webview, &ArticleWebView::linkClicked, this, &ArticleView::linkClicked );
connect( webview->page(), &QWebEnginePage::titleChanged, this, &ArticleView::handleTitleChanged );
connect( webview, &QWidget::customContextMenuRequested, this, &ArticleView::contextMenuRequested );
connect( webview->page(), &QWebEnginePage::linkHovered, this, &ArticleView::linkHovered );
connect( webview, &ArticleWebView::doubleClicked, this, &ArticleView::doubleClicked );
pasteAction.setShortcut( QKeySequence::Paste );
webview->addAction( &pasteAction );
connect( &pasteAction, &QAction::triggered, this, &ArticleView::pasteTriggered );
articleUpAction.setShortcut( QKeySequence( "Alt+Up" ) );
webview->addAction( &articleUpAction );
connect( &articleUpAction, &QAction::triggered, this, &ArticleView::moveOneArticleUp );
articleDownAction.setShortcut( QKeySequence( "Alt+Down" ) );
webview->addAction( &articleDownAction );
connect( &articleDownAction, &QAction::triggered, this, &ArticleView::moveOneArticleDown );
selectCurrentArticleAction.setShortcut( QKeySequence( "Ctrl+Shift+A" ) );
selectCurrentArticleAction.setText( tr( "Select Current Article" ) );
webview->addAction( &selectCurrentArticleAction );
connect( &selectCurrentArticleAction, &QAction::triggered, this, &ArticleView::selectCurrentArticle );
copyAsTextAction.setShortcut( QKeySequence( "Ctrl+Shift+C" ) );
copyAsTextAction.setText( tr( "Copy as text" ) );
webview->addAction( &copyAsTextAction );
connect( &copyAsTextAction, &QAction::triggered, this, &ArticleView::copyAsText );
inspectAction.setShortcut( QKeySequence( Qt::Key_F12 ) );
inspectAction.setText( tr( "Inspect" ) );
webview->addAction( &inspectAction );
connect( &inspectAction, &QAction::triggered, this, &ArticleView::inspectElement );
webview->installEventFilter( this );
searchPanel->installEventFilter( this );
ftsSearchPanel->installEventFilter( this );
QWebEngineSettings * settings = webview->settings();
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::LocalContentCanAccessFileUrls, true );
settings->setAttribute( QWebEngineSettings::ErrorPageEnabled, false );
settings->setAttribute( QWebEngineSettings::LinksIncludedInFocusChain, false );
settings->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false );
settings->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true );
settings->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false );
#endif
auto html = articleNetMgr.getHtml( ResourceType::UNTITLE );
webview->setHtml( QString::fromStdString( html ) );
expandOptionalParts = cfg.preferences.alwaysExpandOptionalParts;
#ifndef Q_OS_MACOS
webview->grabGesture( Gestures::GDPinchGestureType );
webview->grabGesture( Gestures::GDSwipeGestureType );
#endif
this->webview->setHtml( QString::fromStdString( html ) );
connect( GlobalBroadcaster::instance(), &GlobalBroadcaster::dictionaryChanges, this, &ArticleView::setActiveDictIds ); connect( GlobalBroadcaster::instance(), &GlobalBroadcaster::dictionaryChanges, this, &ArticleView::setActiveDictIds );
connect( GlobalBroadcaster::instance(), &GlobalBroadcaster::dictionaryClear, this, &ArticleView::dictionaryClear ); connect( GlobalBroadcaster::instance(), &GlobalBroadcaster::dictionaryClear, this, &ArticleView::dictionaryClear );
@ -304,6 +338,7 @@ void ArticleView::showDefinition( QString const & word,
Contexts const & contexts_ ) Contexts const & contexts_ )
{ {
GlobalBroadcaster::instance()->pronounce_engine.reset(); GlobalBroadcaster::instance()->pronounce_engine.reset();
clearWebsiteTabs();
currentWord = word.trimmed(); currentWord = word.trimmed();
if ( currentWord.isEmpty() ) { if ( currentWord.isEmpty() ) {
return; return;
@ -351,7 +386,7 @@ void ArticleView::showDefinition( QString const & word,
QString mutedDicts = getMutedForGroup( group ); QString mutedDicts = getMutedForGroup( group );
if ( mutedDicts.size() ) { if ( !mutedDicts.isEmpty() ) {
Utils::Url::addQueryItem( req, "muted", mutedDicts ); Utils::Url::addQueryItem( req, "muted", mutedDicts );
} }
@ -1235,6 +1270,17 @@ void ArticleView::syncBackgroundColorWithCfgDarkReader() const
#endif #endif
} }
void ArticleView::openWebsiteInNewTab( QString name, QString url )
{
QString escaped = Utils::escapeAmps( name );
//found existed QWebEngineView.
auto * view = new QWebEngineView( this );
view->load( QUrl( url ) );
tabWidget->addTab( view, escaped );
}
void ArticleView::back() void ArticleView::back()
{ {
@ -2162,6 +2208,11 @@ void ArticleView::clearContent()
webview->setHtml( QString::fromStdString( html ) ); webview->setHtml( QString::fromStdString( html ) );
} }
void ArticleView::load( QString url )
{
webview->load( QUrl( url ) );
}
ResourceToSaveHandler::ResourceToSaveHandler( ArticleView * view, QString fileName ): ResourceToSaveHandler::ResourceToSaveHandler( ArticleView * view, QString fileName ):
QObject( view ), QObject( view ),

View file

@ -96,7 +96,7 @@ public:
QAction * dictionaryBarToggled = nullptr, QAction * dictionaryBarToggled = nullptr,
unsigned currentGroupId = 0 ); unsigned currentGroupId = 0 );
void openWebsiteInNewTab( QString name, QString url );
void setCurrentGroupId( unsigned currengGrgId ); void setCurrentGroupId( unsigned currengGrgId );
unsigned getCurrentGroupId(); unsigned getCurrentGroupId();
@ -108,7 +108,7 @@ public:
~ArticleView(); ~ArticleView();
void load( QString url );
/// Returns "gdfrom-" + dictionaryId. /// Returns "gdfrom-" + dictionaryId.
static QString scrollToFromDictionaryId( QString const & dictionaryId ); static QString scrollToFromDictionaryId( QString const & dictionaryId );
@ -167,9 +167,14 @@ public:
/// \brief Set background as black if darkreader mode is enabled. /// \brief Set background as black if darkreader mode is enabled.
void syncBackgroundColorWithCfgDarkReader() const; void syncBackgroundColorWithCfgDarkReader() const;
void addWebsiteTab( QString name, QString url );
void clearWebsiteTabs();
private: private:
// widgets // widgets
ArticleWebView * webview; ArticleWebView * webview;
QTabWidget * tabWidget;
SearchPanel * searchPanel; SearchPanel * searchPanel;
FtsSearchPanel * ftsSearchPanel; FtsSearchPanel * ftsSearchPanel;
@ -411,6 +416,7 @@ private:
QString getMutedForGroup( unsigned group ); QString getMutedForGroup( unsigned group );
QStringList getMutedDictionaries( unsigned group ); QStringList getMutedDictionaries( unsigned group );
void setupWebview();
}; };
class ResourceToSaveHandler: public QObject class ResourceToSaveHandler: public QObject

View file

@ -66,6 +66,7 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QWebEngineSettings> #include <QWebEngineSettings>
#include <QProxyStyle> #include <QProxyStyle>
#include <utility>
#ifdef HAVE_X11 #ifdef HAVE_X11
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) #if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
@ -401,15 +402,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
connect( trayIconMenu.addAction( tr( "Show &Main Window" ) ), #ifndef Q_OS_MACOS // macOS uses the dock menu instead of the tray icon
&QAction::triggered, connect( trayIconMenu.addAction( tr( "Show &Main Window" ) ), &QAction::triggered, this, [ this ] {
this, this->toggleMainWindow( true );
&MainWindow::showMainWindow ); } );
#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();
@ -716,6 +720,10 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
&GlobalBroadcaster::indexingDictionary, &GlobalBroadcaster::indexingDictionary,
this, this,
&MainWindow::showFTSIndexingName ); &MainWindow::showFTSIndexingName );
connect( GlobalBroadcaster::instance(),
&GlobalBroadcaster::websiteDictionarySignal,
this,
&MainWindow::openWebsiteInNewTab );
connect( &GlobalBroadcaster::instance()->pronounce_engine, connect( &GlobalBroadcaster::instance()->pronounce_engine,
&PronounceEngine::emitAudio, &PronounceEngine::emitAudio,
@ -1421,6 +1429,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;
@ -1444,6 +1457,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 )
@ -2147,7 +2161,7 @@ void MainWindow::updateFoundInDictsList()
if ( dictionaries[ x ]->getId() == i->toUtf8().data() ) { if ( dictionaries[ x ]->getId() == i->toUtf8().data() ) {
QString dictName = QString::fromUtf8( dictionaries[ x ]->getName().c_str() ); QString dictName = QString::fromUtf8( dictionaries[ x ]->getName().c_str() );
QString dictId = QString::fromUtf8( dictionaries[ x ]->getId().c_str() ); QString dictId = QString::fromUtf8( dictionaries[ x ]->getId().c_str() );
QListWidgetItem * item = auto * item =
new QListWidgetItem( dictionaries[ x ]->getIcon(), dictName, ui.dictsList, QListWidgetItem::Type ); new QListWidgetItem( dictionaries[ x ]->getIcon(), dictName, ui.dictsList, QListWidgetItem::Type );
item->setData( Qt::UserRole, QVariant( dictId ) ); item->setData( Qt::UserRole, QVariant( dictId ) );
item->setToolTip( dictName ); item->setToolTip( dictName );
@ -2161,7 +2175,7 @@ void MainWindow::updateFoundInDictsList()
} }
} }
//if no item in dict List panel has been choose ,select first one. //if no item in dict List panel has been choosen ,select first one.
if ( ui.dictsList->count() > 0 && ui.dictsList->selectedItems().empty() ) { if ( ui.dictsList->count() > 0 && ui.dictsList->selectedItems().empty() ) {
ui.dictsList->setCurrentRow( 0 ); ui.dictsList->setCurrentRow( 0 );
} }
@ -2172,7 +2186,7 @@ void MainWindow::updateBackForwardButtons()
{ {
ArticleView * view = getCurrentArticleView(); ArticleView * view = getCurrentArticleView();
if ( view ) { if ( view != nullptr ) {
navBack->setEnabled( view->canGoBack() ); navBack->setEnabled( view->canGoBack() );
navForward->setEnabled( view->canGoForward() ); navForward->setEnabled( view->canGoForward() );
} }
@ -2181,7 +2195,12 @@ void MainWindow::updateBackForwardButtons()
void MainWindow::updatePronounceAvailability() void MainWindow::updatePronounceAvailability()
{ {
if ( ui.tabWidget->count() > 0 ) { if ( ui.tabWidget->count() > 0 ) {
getCurrentArticleView()->hasSound( [ this ]( bool has ) { ArticleView * pView = getCurrentArticleView();
if ( pView == nullptr ) {
return;
}
pView->hasSound( [ this ]( bool has ) {
navPronounce->setEnabled( has ); navPronounce->setEnabled( has );
} ); } );
} }
@ -2532,7 +2551,7 @@ void MainWindow::handleEsc()
} }
if ( cfg.preferences.escKeyHidesMainWindow ) { if ( cfg.preferences.escKeyHidesMainWindow ) {
toggleMainWindow(); toggleMainWindow( false );
} }
else { else {
focusTranslateLine(); focusTranslateLine();
@ -2873,7 +2892,7 @@ void MainWindow::showTranslationForDicts( QString const & inWord,
ignoreDiacritics ); ignoreDiacritics );
} }
void MainWindow::toggleMainWindow( bool onlyShow ) void MainWindow::toggleMainWindow( bool ensureShow )
{ {
bool shown = false; bool shown = false;
@ -2906,7 +2925,7 @@ void MainWindow::toggleMainWindow( bool onlyShow )
} }
shown = true; shown = true;
} }
else if ( !onlyShow ) { else if ( !ensureShow ) {
// On Windows and Linux, a hidden window won't show a task bar icon // On Windows and Linux, a hidden window won't show a task bar icon
// When trayicon is enabled, the duplication is unneeded // When trayicon is enabled, the duplication is unneeded
@ -2990,7 +3009,7 @@ void MainWindow::installHotKeys()
void MainWindow::hotKeyActivated( int hk ) void MainWindow::hotKeyActivated( int hk )
{ {
if ( !hk ) { if ( !hk ) {
toggleMainWindow(); toggleMainWindow( false );
} }
else if ( scanPopup ) { else if ( scanPopup ) {
#ifdef HAVE_X11 #ifdef HAVE_X11
@ -3075,7 +3094,7 @@ void MainWindow::trayIconActivated( QSystemTrayIcon::ActivationReason r )
switch ( r ) { switch ( r ) {
case QSystemTrayIcon::Trigger: case QSystemTrayIcon::Trigger:
// Left click toggles the visibility of main window // Left click toggles the visibility of main window
toggleMainWindow(); toggleMainWindow( false );
break; break;
case QSystemTrayIcon::MiddleClick: case QSystemTrayIcon::MiddleClick:
@ -3088,10 +3107,6 @@ void MainWindow::trayIconActivated( QSystemTrayIcon::ActivationReason r )
} }
} }
void MainWindow::showMainWindow()
{
toggleMainWindow( true );
}
void MainWindow::visitHomepage() void MainWindow::visitHomepage()
{ {
@ -3718,7 +3733,8 @@ void MainWindow::messageFromAnotherInstanceReceived( QString const & message )
ArticleView * MainWindow::getCurrentArticleView() ArticleView * MainWindow::getCurrentArticleView()
{ {
if ( QWidget * cw = ui.tabWidget->currentWidget() ) { if ( QWidget * cw = ui.tabWidget->currentWidget() ) {
return dynamic_cast< ArticleView * >( cw ); auto * pView = dynamic_cast< ArticleView * >( cw );
return pView;
} }
return nullptr; return nullptr;
} }
@ -3730,13 +3746,6 @@ void MainWindow::wordReceived( const QString & word )
respondToTranslationRequest( word, false ); respondToTranslationRequest( word, false );
} }
void MainWindow::headwordReceived( const QString & word, const QString & ID )
{
toggleMainWindow( true );
setInputLineText( word, WildcardPolicy::EscapeWildcards, NoPopupChange );
respondToTranslationRequest( word, false, ArticleView::scrollToFromDictionaryId( ID ), false );
}
void MainWindow::updateFavoritesMenu() void MainWindow::updateFavoritesMenu()
{ {
if ( ui.favoritesPane->isVisible() ) { if ( ui.favoritesPane->isVisible() ) {
@ -4141,7 +4150,13 @@ void MainWindow::showDictionaryHeadwords( Dictionary::Class * dict )
headwordsDlg = new DictHeadwords( this, cfg, dict ); headwordsDlg = new DictHeadwords( this, cfg, dict );
addGlobalActionsToDialog( headwordsDlg ); addGlobalActionsToDialog( headwordsDlg );
addGroupComboBoxActionsToDialog( headwordsDlg, groupList ); addGroupComboBoxActionsToDialog( headwordsDlg, groupList );
connect( headwordsDlg, &DictHeadwords::headwordSelected, this, &MainWindow::headwordReceived ); connect( headwordsDlg,
&DictHeadwords::headwordSelected,
this,
[ this ]( QString const & headword, QString const & dictID ) {
setInputLineText( headword, WildcardPolicy::EscapeWildcards, NoPopupChange );
respondToTranslationRequest( headword, false, ArticleView::scrollToFromDictionaryId( dictID ), false );
} );
connect( headwordsDlg, connect( headwordsDlg,
&DictHeadwords::closeDialog, &DictHeadwords::closeDialog,
this, this,
@ -4374,6 +4389,19 @@ void MainWindow::showFTSIndexingName( QString const & name )
} }
} }
void MainWindow::openWebsiteInNewTab( QString name, QString url )
{
// QString escaped = Utils::escapeAmps( name );
// auto * view = new ArticleView( this, articleNetMgr, audioPlayerFactory.player(), cfg );
// view->load( url );
// int index = cfg.preferences.newTabsOpenAfterCurrentOne ? ui.tabWidget->currentIndex() + 1 : ui.tabWidget->count();
// ui.tabWidget->insertTab( index, view, escaped );
// mruList.append( dynamic_cast< QWidget * >( view ) );
getCurrentArticleView()->addWebsiteTab( std::move( name ), url );
}
QString MainWindow::unescapeTabHeader( QString const & header ) QString MainWindow::unescapeTabHeader( QString const & header )
{ {
// Reset table header to original headword // Reset table header to original headword

View file

@ -67,7 +67,6 @@ public slots:
void messageFromAnotherInstanceReceived( QString const & ); void messageFromAnotherInstanceReceived( QString const & );
void showStatusBarMessage( QString const &, int, QPixmap const & ); void showStatusBarMessage( QString const &, int, QPixmap const & );
void wordReceived( QString const & ); void wordReceived( QString const & );
void headwordReceived( QString const &, QString const & );
void headwordFromFavorites( QString const &, QString const & ); void headwordFromFavorites( QString const &, QString const & );
void quitApp(); void quitApp();
@ -226,9 +225,8 @@ private:
/// group, or to all dictionaries if there are no groups. /// group, or to all dictionaries if there are no groups.
vector< sptr< Dictionary::Class > > const & getActiveDicts(); vector< sptr< Dictionary::Class > > const & getActiveDicts();
/// Brings the main window to front if it's not currently, or hides it /// @param ensureShow only ensure the window will be shown and no "toggling"
/// otherwise. The hiding part is omitted if onlyShow is true. void toggleMainWindow( bool ensureShow );
void toggleMainWindow( bool onlyShow = false );
/// Creates hotkeyWrapper and hooks the currently set keys for it /// Creates hotkeyWrapper and hooks the currently set keys for it
void installHotKeys(); void installHotKeys();
@ -292,6 +290,7 @@ private slots:
void openDictionaryFolder( QString const & id ); void openDictionaryFolder( QString const & id );
void showFTSIndexingName( QString const & name ); void showFTSIndexingName( QString const & name );
void openWebsiteInNewTab( QString name, QString url );
void handleAddToFavoritesButton(); void handleAddToFavoritesButton();
@ -398,8 +397,6 @@ private slots:
void setAutostart( bool ); void setAutostart( bool );
void showMainWindow();
void visitHomepage(); void visitHomepage();
void visitForum(); void visitForum();
void openConfigFolder(); void openConfigFolder();

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 );
@ -361,6 +366,7 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
//Misc //Misc
ui.removeInvalidIndexOnExit->setChecked( p.removeInvalidIndexOnExit ); ui.removeInvalidIndexOnExit->setChecked( p.removeInvalidIndexOnExit );
ui.openWebsiteInNewTab->setChecked( p.openWebsiteInNewTab );
// Add-on styles // Add-on styles
ui.addonStylesLabel->setVisible( ui.addonStyles->count() > 1 ); ui.addonStylesLabel->setVisible( ui.addonStyles->count() > 1 );
@ -520,6 +526,7 @@ Config::Preferences Preferences::getPreferences()
p.clearNetworkCacheOnExit = ui.clearNetworkCacheOnExit->isChecked(); p.clearNetworkCacheOnExit = ui.clearNetworkCacheOnExit->isChecked();
p.removeInvalidIndexOnExit = ui.removeInvalidIndexOnExit->isChecked(); p.removeInvalidIndexOnExit = ui.removeInvalidIndexOnExit->isChecked();
p.openWebsiteInNewTab = ui.openWebsiteInNewTab->isChecked();
p.addonStyle = ui.addonStyles->getCurrentStyle(); p.addonStyle = ui.addonStyles->getCurrentStyle();

View file

@ -1938,6 +1938,13 @@ from Stardict, Babylon and GLS dictionaries</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="openWebsiteInNewTab">
<property name="text">
<string>Open website dictionary in seperate tab</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>