Compare commits

...

20 commits

Author SHA1 Message Date
xiaoyifang 18ff0458b5
Merge c0697ecdda into dcc4579544 2024-11-08 10:39:01 -05:00
xiaoyifang dcc4579544
Merge pull request #1927 from xiaoyifang/opt/group-empty-name
Some checks failed
SonarCloud / Build and analyze (push) Has been cancelled
opt: Group struct default constructor only invoked with empty string
2024-11-08 20:59:40 +08:00
xiaoyifang d3ad40f988
Merge pull request #1924 from xiaoyifang/opt/tab-blank
opt: dictionary tab switch will show blank widget when source changing.
2024-11-08 20:55:42 +08:00
xiaoyifang 081cba6b23 opt: Group struct default constructor only invoked with empty string 2024-11-08 20:49:55 +08:00
xiaoyifang 59f9b3d04f opt: dictionary tab switch show blank widget when source changed 2024-11-08 20:48:31 +08:00
shenleban tongying f6434f4219 fix a mistake of https://github.com/xiaoyifang/goldendict-ng/pull/1912
Some checks are pending
SonarCloud / Build and analyze (push) Waiting to run
2024-11-08 01:36:29 -05:00
shenleban tongying f694c3210e clean: QLocale country -> territory renaming
Some checks failed
SonarCloud / Build and analyze (push) Waiting to run
deploy_website / deploy (push) Has been cancelled
2024-11-07 03:01:45 -05: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
18 changed files with 270 additions and 164 deletions

View file

@ -529,6 +529,22 @@ 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()
-> websiteDictionary( QString::fromStdString( activeDict->getName() ), requestUrl );
continue;
}
}
sptr< Dictionary::DataRequest > r = activeDict->getArticle( sptr< Dictionary::DataRequest > r = activeDict->getArticle(
wordStd, wordStd,
altsVector, altsVector,

View file

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

View file

@ -111,7 +111,7 @@ HotKey::HotKey( QKeySequence const & seq ):
QKeySequence HotKey::toKeySequence() const QKeySequence HotKey::toKeySequence() const
{ {
if ( key2 != 0 || key2 != Qt::Key::Key_unknown ) { if ( key2 != 0 && key2 != Qt::Key::Key_unknown ) {
return { QKeyCombination( modifiers, static_cast< Qt::Key >( key1 ) ), return { QKeyCombination( modifiers, static_cast< Qt::Key >( key1 ) ),
QKeyCombination( modifiers, static_cast< Qt::Key >( key2 ) ) }; QKeyCombination( modifiers, static_cast< Qt::Key >( key2 ) ) };
} }
@ -1073,6 +1073,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 +2106,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

@ -392,6 +392,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

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

@ -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,14 +330,12 @@ 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 ), fmt::format_to( std::back_inserter( result ),
R"(<iframe id="gdexpandframe-{}" src="{}" R"(<iframe id="gdexpandframe-{}" src="{}"
onmouseover="processIframeMouseOver('gdexpandframe-{}');" onmouseover="processIframeMouseOver('gdexpandframe-{}');"
@ -352,7 +345,7 @@ 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

@ -53,9 +53,8 @@ Group::Group( Config::Group const & cfgGroup,
} }
} }
Group::Group( QString name_ ): Group::Group():
id( 0 ), id( 0 )
name( std::move( name_ ) )
{ {
} }

View file

@ -32,7 +32,7 @@ struct Group
Config::Group const & inactiveGroup ); Config::Group const & inactiveGroup );
/// Creates an empty group. /// Creates an empty group.
explicit Group( QString name_ ); explicit Group();
Group( unsigned id, QString name_ ); Group( unsigned id, QString name_ );

View file

@ -39,7 +39,7 @@ SpeechClient::Engines SpeechClient::availableEngines()
for ( const QVoice & voice : sp->availableVoices() ) { for ( const QVoice & voice : sp->availableVoices() ) {
const QString name( QString( "%4 - %3 %1 (%2)" ) const QString name( QString( "%4 - %3 %1 (%2)" )
.arg( QLocale::languageToString( locale.language() ), .arg( QLocale::languageToString( locale.language() ),
( QLocale::countryToString( locale.country() ) ), ( QLocale::territoryToString( locale.territory() ) ),
voice.name(), voice.name(),
engine_name ) ); engine_name ) );
Engine engine( Config::VoiceEngine( engine_name, name, voice.name(), QLocale( locale ), 50, 0 ) ); Engine engine( Config::VoiceEngine( engine_name, name, voice.name(), QLocale( locale ), 50, 0 ) );

View file

@ -85,7 +85,154 @@ QString ArticleView::scrollToFromDictionaryId( QString const & dictionaryId )
Q_ASSERT( !isScrollTo( dictionaryId ) ); Q_ASSERT( !isScrollTo( dictionaryId ) );
return scrollToPrefix + dictionaryId; return scrollToPrefix + dictionaryId;
} }
ArticleView::ArticleView( QWidget * parent,
ArticleNetworkAccessManager & nm,
AudioPlayerPtr const & audioPlayer_,
Config::Class const & cfg_ ):
QWidget( parent ),
articleNetMgr( nm ),
audioPlayer( audioPlayer_ ),
cfg( cfg_ )
{
// setup GUI
setupWebview();
}
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->addWidget( this->tabWidget );
QWidget * 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
}
ArticleView::ArticleView( QWidget * parent, ArticleView::ArticleView( QWidget * parent,
ArticleNetworkAccessManager & nm, ArticleNetworkAccessManager & nm,
AudioPlayerPtr const & audioPlayer_, AudioPlayerPtr const & audioPlayer_,
@ -115,133 +262,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 );
@ -1235,6 +1260,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 +2198,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

@ -95,7 +95,10 @@ public:
QLineEdit const * translateLine, QLineEdit const * translateLine,
QAction * dictionaryBarToggled = nullptr, QAction * dictionaryBarToggled = nullptr,
unsigned currentGroupId = 0 ); unsigned currentGroupId = 0 );
explicit ArticleView( QWidget * parent,
ArticleNetworkAccessManager & nm,
AudioPlayerPtr const & audioPlayer_,
Config::Class const & cfg_ );
void setCurrentGroupId( unsigned currengGrgId ); void setCurrentGroupId( unsigned currengGrgId );
unsigned getCurrentGroupId(); unsigned getCurrentGroupId();
@ -108,7 +111,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 +170,12 @@ 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 openWebsiteInNewTab( QString name, QString url );
private: private:
// widgets // widgets
ArticleWebView * webview; ArticleWebView * webview;
QTabWidget * tabWidget;
SearchPanel * searchPanel; SearchPanel * searchPanel;
FtsSearchPanel * ftsSearchPanel; FtsSearchPanel * ftsSearchPanel;
@ -411,6 +417,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

@ -200,7 +200,7 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
#ifndef NO_TTS_SUPPORT #ifndef NO_TTS_SUPPORT
cfg.voiceEngines = sources.getVoiceEngines(); cfg.voiceEngines = sources.getVoiceEngines();
#endif #endif
ui.tabs->setUpdatesEnabled( false ); setUpdatesEnabled( false );
// Those hold pointers to dictionaries, we need to free them. // Those hold pointers to dictionaries, we need to free them.
groupInstances.clear(); groupInstances.clear();
@ -225,7 +225,7 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
connect( groups, &Groups::showDictionaryInfo, this, &EditDictionaries::showDictionaryInfo ); connect( groups, &Groups::showDictionaryInfo, this, &EditDictionaries::showDictionaryInfo );
connect( orderAndProps, &OrderAndProps::showDictionaryHeadwords, this, &EditDictionaries::showDictionaryHeadwords ); connect( orderAndProps, &OrderAndProps::showDictionaryHeadwords, this, &EditDictionaries::showDictionaryHeadwords );
} }
ui.tabs->setUpdatesEnabled( true ); setUpdatesEnabled( true );
} }
EditDictionaries::~EditDictionaries() EditDictionaries::~EditDictionaries()
{ {

View file

@ -122,7 +122,7 @@ void DictGroupWidget::groupIconActivated( int index )
Config::Group DictGroupWidget::makeGroup() const Config::Group DictGroupWidget::makeGroup() const
{ {
Instances::Group g( "" ); Instances::Group g;
g.id = groupId; g.id = groupId;

View file

@ -716,6 +716,10 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
&GlobalBroadcaster::indexingDictionary, &GlobalBroadcaster::indexingDictionary,
this, this,
&MainWindow::showFTSIndexingName ); &MainWindow::showFTSIndexingName );
connect( GlobalBroadcaster::instance(),
&GlobalBroadcaster::websiteDictionary,
this,
&MainWindow::openWebsiteInNewTab );
connect( &GlobalBroadcaster::instance()->pronounce_engine, connect( &GlobalBroadcaster::instance()->pronounce_engine,
&PronounceEngine::emitAudio, &PronounceEngine::emitAudio,
@ -2172,7 +2176,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 +2185,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 );
} ); } );
} }
@ -3718,7 +3727,13 @@ 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 );
if ( pView != nullptr ) {
if ( pView->getParentView() != nullptr ) {
return pView->getParentView();
}
}
return pView;
} }
return nullptr; return nullptr;
} }
@ -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( 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

@ -292,6 +292,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();

View file

@ -135,7 +135,7 @@ OrderAndProps::OrderAndProps( QWidget * parent,
Config::Group OrderAndProps::getCurrentDictionaryOrder() const Config::Group OrderAndProps::getCurrentDictionaryOrder() const
{ {
Instances::Group g( "" ); Instances::Group g;
g.dictionaries = ui.dictionaryOrder->getCurrentDictionaries(); g.dictionaries = ui.dictionaryOrder->getCurrentDictionaries();
@ -144,7 +144,7 @@ Config::Group OrderAndProps::getCurrentDictionaryOrder() const
Config::Group OrderAndProps::getCurrentInactiveDictionaries() const Config::Group OrderAndProps::getCurrentInactiveDictionaries() const
{ {
Instances::Group g( "" ); Instances::Group g;
g.dictionaries = ui.inactiveDictionaries->getCurrentDictionaries(); g.dictionaries = ui.inactiveDictionaries->getCurrentDictionaries();

View file

@ -361,6 +361,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 +521,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>