Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opt: dictionarygroup refactor in articleview #1625

Merged
merged 10 commits into from
Jul 3, 2024
2 changes: 2 additions & 0 deletions goldendict.pro
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ HEADERS += \
src/iframeschemehandler.hh \
src/indexedzip.hh \
src/initializing.hh \
src/dictionary_group.hh \
src/instances.hh \
src/keyboardstate.hh \
src/langcoder.hh \
Expand Down Expand Up @@ -484,6 +485,7 @@ SOURCES += \
src/iframeschemehandler.cc \
src/indexedzip.cc \
src/initializing.cc \
src/dictionary_group.cc \
src/instances.cc \
src/keyboardstate.cc \
src/langcoder.cc \
Expand Down
50 changes: 50 additions & 0 deletions src/dictionary_group.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* Licensed under GPLv3 or later, see the LICENSE file */


#include "dictionary_group.hh"


sptr< Dictionary::Class > DictionaryGroup::getDictionaryByName( QString const & dictName )
{
// Link to other dictionary
for ( const auto & allDictionarie : allDictionaries ) {
if ( dictName.compare( QString::fromUtf8( allDictionarie->getName().c_str() ) ) == 0 ) {
return allDictionarie;
}
}
return nullptr;
}

const std::vector< sptr< Dictionary::Class > > * DictionaryGroup::getActiveDictionaries( unsigned currentGroup )
{
std::vector< sptr< Dictionary::Class > > const * activeDicts = nullptr;

if ( !groups.empty() ) {
for ( const auto & group : groups )
if ( group.id == currentGroup ) {
activeDicts = &( group.dictionaries );
break;
}
}
else
activeDicts = &allDictionaries;

return activeDicts;
}


sptr< Dictionary::Class > DictionaryGroup::getDictionaryById( const std::string & dictId )
{

for ( unsigned x = allDictionaries.size(); x--; ) {
if ( allDictionaries[ x ]->getId() == dictId ) {
return allDictionaries[ x ];
}
}
return nullptr;
}

Instances::Group const * DictionaryGroup::getGroupById( unsigned groupId )
{
return groups.findGroup( groupId );
}
34 changes: 34 additions & 0 deletions src/dictionary_group.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* Licensed under GPLv3 or later, see the LICENSE file */

#ifndef DICTIONARYGROUP_HH_INCLUDED
#define DICTIONARYGROUP_HH_INCLUDED

#include "sptr.hh"
#include "dict/dictionary.hh"
#include "instances.hh"

class DictionaryGroup
{
public:
DictionaryGroup( std::vector< sptr< Dictionary::Class > > const & allDictionaries_,
Instances::Groups const & groups_ ):
allDictionaries( allDictionaries_ ),
groups( groups_ )
{
}

sptr< Dictionary::Class > getDictionaryByName( QString const & dictionaryName );

const std::vector< sptr< Dictionary::Class > > * getActiveDictionaries( unsigned groupId );

sptr< Dictionary::Class > getDictionaryById( const std::string & dictId );

Instances::Group const * getGroupById( unsigned groupId );


private:
std::vector< sptr< Dictionary::Class > > const & allDictionaries;
Instances::Groups const & groups;
};

#endif // DICTIONARYGROUP_HH_INCLUDED
89 changes: 32 additions & 57 deletions src/ui/articleview.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@
QWidget( parent ),
articleNetMgr( nm ),
audioPlayer( audioPlayer_ ),
allDictionaries( allDictionaries_ ),
groups( groups_ ),
dictionaryGroup( std::make_unique< DictionaryGroup >( allDictionaries_, groups_ ) ),
popupView( popupView_ ),
cfg( cfg_ ),
pasteAction( this ),
Expand Down Expand Up @@ -750,7 +749,7 @@
{
if ( dictionaryBarToggled && dictionaryBarToggled->isChecked() ) {
// Dictionary bar is active -- mute the muted dictionaries
Instances::Group const * groupInstance = groups.findGroup( group );
Instances::Group const * groupInstance = dictionaryGroup->getGroupById( group );

// Find muted dictionaries for current group
Config::Group const * grp = cfg.getGroup( group );
Expand Down Expand Up @@ -784,7 +783,7 @@
{
if ( dictionaryBarToggled && dictionaryBarToggled->isChecked() ) {
// Dictionary bar is active -- mute the muted dictionaries
Instances::Group const * groupInstance = groups.findGroup( group );
Instances::Group const * groupInstance = dictionaryGroup->getGroupById( group );

// Find muted dictionaries for current group
Config::Group const * grp = cfg.getGroup( group );
Expand Down Expand Up @@ -915,273 +914,262 @@
} );
}

void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const & scrollTo, Contexts const & contexts_ )
{
audioPlayer->stop();
qDebug() << "open link url:" << url;

auto [ valid, word ] = Utils::Url::getQueryWord( url );
if ( valid && word.isEmpty() ) {
//if valid=true and word is empty,the url must be a invalid gdlookup url.
//else if valid=false,the url should be external urls.
return;
}

Contexts contexts( contexts_ );

if ( url.scheme().compare( "ankisearch" ) == 0 ) {
ankiConnector->ankiSearch( url.path() );
return;
}
else if ( url.scheme().compare( "ankicard" ) == 0 ) {
// If article id is set in path and selection is empty, use text from the current article.
// Otherwise, grab currently selected text and use it as the definition.
if ( !url.path().isEmpty() && webview->selectedText().isEmpty() ) {
makeAnkiCardFromArticle( url.path() );
}
else {
sendToAnki( webview->title(), webview->selectedText(), translateLine->text() );
}
qDebug() << "requested to make Anki card.";
return;
}
else if ( url.scheme().compare( "bword" ) == 0 || url.scheme().compare( "entry" ) == 0 ) {
if ( Utils::Url::hasQueryItem( ref, "dictionaries" ) ) {
QStringList dictsList = Utils::Url::queryItemValue( ref, "dictionaries" ).split( ",", Qt::SkipEmptyParts );

showDefinition( word, dictsList, getGroup( ref ), false );
}
else
showDefinition( word, getGroup( ref ), scrollTo, contexts );
}
else if ( url.scheme() == "gdlookup" ) // Plain html links inherit gdlookup scheme
{
if ( url.hasFragment() ) {
webview->page()->runJavaScript(
QString( "window.location = \"%1\"" ).arg( QString::fromUtf8( url.toEncoded() ) ) );
}
else {
if ( Utils::Url::hasQueryItem( ref, "dictionaries" ) ) {
// Specific dictionary group from full-text search
QStringList dictsList = Utils::Url::queryItemValue( ref, "dictionaries" ).split( ",", Qt::SkipEmptyParts );

showDefinition( url.path().mid( 1 ), dictsList, getGroup( ref ), false );
return;
}

if ( Utils::Url::hasQueryItem( url, "dictionaries" ) ) {
// Specific dictionary group from full-text search
QStringList dictsList = Utils::Url::queryItemValue( url, "dictionaries" ).split( ",", Qt::SkipEmptyParts );

showDefinition( word, dictsList, getGroup( url ), false );
return;
}

QString newScrollTo( scrollTo );
if ( Utils::Url::hasQueryItem( url, "dict" ) ) {
// Link to other dictionary
QString dictName( Utils::Url::queryItemValue( url, "dict" ) );
for ( const auto & allDictionarie : allDictionaries ) {
if ( dictName.compare( QString::fromUtf8( allDictionarie->getName().c_str() ) ) == 0 ) {
newScrollTo = scrollToFromDictionaryId( QString::fromUtf8( allDictionarie->getId().c_str() ) );
break;
}
auto dict = dictionaryGroup->getDictionaryByName( dictName );
if ( dict ) {
newScrollTo = scrollToFromDictionaryId( QString::fromUtf8( dict->getId().c_str() ) );
}
}

if ( Utils::Url::hasQueryItem( url, "gdanchor" ) )
contexts[ "gdanchor" ] = Utils::Url::queryItemValue( url, "gdanchor" );

showDefinition( word, getGroup( ref ), newScrollTo, contexts );
}
}
else if ( url.scheme() == "bres" || url.scheme() == "gdau" || url.scheme() == "gdvideo"
|| Utils::Url::isAudioUrl( url ) ) {
// Download it

// Clear any pending ones

resourceDownloadRequests.clear();

resourceDownloadUrl = url;

if ( Utils::Url::isWebAudioUrl( url ) ) {
sptr< Dictionary::DataRequest > req = std::make_shared< Dictionary::WebMultimediaDownload >( url, articleNetMgr );

resourceDownloadRequests.push_back( req );

connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
}
else if ( url.scheme() == "gdau" && url.host() == "search" ) {
// Since searches should be limited to current group, we just do them
// here ourselves since otherwise we'd need to pass group id to netmgr
// and it should've been having knowledge of the current groups, too.

unsigned currentGroup = getGroup( ref );

std::vector< sptr< Dictionary::Class > > const * activeDicts = nullptr;

if ( !groups.empty() ) {
for ( const auto & group : groups )
if ( group.id == currentGroup ) {
activeDicts = &( group.dictionaries );
break;
}
}
else
activeDicts = &allDictionaries;
std::vector< sptr< Dictionary::Class > > const * activeDicts =
dictionaryGroup->getActiveDictionaries( currentGroup );

if ( activeDicts ) {
unsigned preferred = UINT_MAX;
if ( url.hasFragment() ) {
// Find sound in the preferred dictionary
QString preferredName = Utils::Url::fragment( url );
try {
for ( unsigned x = 0; x < activeDicts->size(); ++x ) {
if ( preferredName.compare( QString::fromUtf8( ( *activeDicts )[ x ]->getName().c_str() ) ) == 0 ) {
preferred = x;
sptr< Dictionary::DataRequest > req =
( *activeDicts )[ x ]->getResource( url.path().mid( 1 ).toUtf8().data() );

resourceDownloadRequests.push_back( req );

if ( !req->isFinished() ) {
// Queued loading
connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
}
else {
// Immediate loading
if ( req->dataSize() > 0 ) {
// Resource already found, stop next search
resourceDownloadFinished();
return;
}
}
break;
}
}
}
catch ( std::exception & e ) {
emit statusBarMessage( tr( "ERROR: %1" ).arg( e.what() ), 10000, QPixmap( ":/icons/error.svg" ) );
}
}
for ( unsigned x = 0; x < activeDicts->size(); ++x ) {
try {
if ( x == preferred )
continue;

sptr< Dictionary::DataRequest > req =
( *activeDicts )[ x ]->getResource( url.path().mid( 1 ).toUtf8().data() );

resourceDownloadRequests.push_back( req );

if ( !req->isFinished() ) {
// Queued loading
connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
}
else {
// Immediate loading
if ( req->dataSize() > 0 ) {
// Resource already found, stop next search
break;
}
}
}
catch ( std::exception & e ) {
emit statusBarMessage( tr( "ERROR: %1" ).arg( e.what() ), 10000, QPixmap( ":/icons/error.svg" ) );
}
}
}
}
else {
// Normal resource download
QString contentType;

sptr< Dictionary::DataRequest > req = articleNetMgr.getResource( url, contentType );

if ( !req.get() ) {
qDebug() << "request failed: " << url;
// Request failed, fail
}
else if ( req->isFinished() && req->dataSize() >= 0 ) {
// Have data ready, handle it
resourceDownloadRequests.push_back( req );
resourceDownloadFinished();

return;
}
else if ( !req->isFinished() ) {
// Queue to be handled when done

resourceDownloadRequests.push_back( req );

connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
}
}

if ( resourceDownloadRequests.empty() ) // No requests were queued
{
qDebug() << tr( "The referenced resource doesn't exist." );
return;
}
else
resourceDownloadFinished(); // Check any requests finished already
}
else if ( url.scheme() == "gdprg" ) {
// Program. Run it.
QString id( url.host() );

for ( const auto & program : cfg.programs ) {
if ( program.id == id ) {
// Found the corresponding program.
Programs::RunInstance * req = new Programs::RunInstance;

connect( req, &Programs::RunInstance::finished, req, &QObject::deleteLater );

QString error;

// Delete the request if it fails to start
if ( !req->start( program, url.path().mid( 1 ), error ) ) {
delete req;

QMessageBox::critical( this, "GoldenDict", error );
}

return;
}
}

// Still here? No such program exists.
QMessageBox::critical( this, "GoldenDict", tr( "The referenced audio program doesn't exist." ) );
}
else if ( url.scheme() == "gdtts" ) {
#ifndef NO_TTS_SUPPORT
// Text to speech
QString md5Id = Utils::Url::queryItemValue( url, "engine" );
QString text( url.path().mid( 1 ) );

for ( const auto & voiceEngine : cfg.voiceEngines ) {
QString itemMd5Id =
QString( QCryptographicHash::hash( voiceEngine.name.toUtf8(), QCryptographicHash::Md5 ).toHex() );

if ( itemMd5Id == md5Id ) {
SpeechClient * speechClient = new SpeechClient( voiceEngine, this );
connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) );
speechClient->tell( text );
break;
}
}
#else
qDebug() << "gdtts:// is not supported due to missing TTS support";
#endif
}

else if ( Utils::isExternalLink( url ) ) {
// Use the system handler for the conventional external links
QDesktopServices::openUrl( url );
}
}

Check notice on line 1172 in src/ui/articleview.cc

View check run for this annotation

codefactor.io / CodeFactor

src/ui/articleview.cc#L917-L1172

Complex Method
ResourceToSaveHandler * ArticleView::saveResource( const QUrl & url, const QString & fileName )
{
return saveResource( url, webview->url(), fileName );
Expand All @@ -1200,17 +1188,8 @@

unsigned currentGroup = getGroup( ref );

std::vector< sptr< Dictionary::Class > > const * activeDicts = nullptr;

if ( groups.size() ) {
for ( const auto & group : groups )
if ( group.id == currentGroup ) {
activeDicts = &( group.dictionaries );
break;
}
}
else
activeDicts = &allDictionaries;
std::vector< sptr< Dictionary::Class > > const * activeDicts =
dictionaryGroup->getActiveDictionaries( currentGroup );

if ( activeDicts ) {
unsigned preferred = UINT_MAX;
Expand Down Expand Up @@ -1570,7 +1549,7 @@
menu.addAction( addWordToHistoryAction );

Instances::Group const * altGroup =
( currentGroupId != getGroup( webview->url() ) ) ? groups.findGroup( currentGroupId ) : nullptr;
( currentGroupId != getGroup( webview->url() ) ) ? dictionaryGroup->getGroupById( currentGroupId ) : nullptr;

if ( altGroup ) {
QIcon icon = altGroup->icon.size() ? QIcon( ":/flags/" + altGroup->icon ) : QIcon();
Expand Down Expand Up @@ -1607,7 +1586,7 @@
if ( txt.size() > 60 )
txt = txt.left( 60 ) + "...";

addHeaderToHistoryAction = new QAction( tr( "&Add \"%1\" to history" ).arg( txt ), &menu );
addHeaderToHistoryAction = new QAction( tr( R"(&Add "%1" to history)" ).arg( txt ), &menu );
menu.addAction( addHeaderToHistoryAction );
}

Expand All @@ -1625,7 +1604,7 @@
// Add table of contents
QStringList ids = getArticlesList();

if ( !menu.isEmpty() && ids.size() )
if ( !menu.isEmpty() && !ids.empty() )
menu.addSeparator();

unsigned refsAdded = 0;
Expand All @@ -1634,31 +1613,27 @@
for ( QStringList::const_iterator i = ids.constBegin(); i != ids.constEnd(); ++i, ++refsAdded ) {
// Find this dictionary

for ( unsigned x = allDictionaries.size(); x--; ) {
if ( allDictionaries[ x ]->getId() == i->toUtf8().data() ) {
QAction * action = nullptr;
if ( refsAdded == cfg.preferences.maxDictionaryRefsInContextMenu ) {
// Enough! Or the menu would become too large.
maxDictionaryRefsAction = new QAction( ".........", &menu );
action = maxDictionaryRefsAction;
maxDictionaryRefsReached = true;
}
else {
action = new QAction( allDictionaries[ x ]->getIcon(),
QString::fromUtf8( allDictionaries[ x ]->getName().c_str() ),
&menu );
// Force icons in menu on all platforms,
// since without them it will be much harder
// to find things.
action->setIconVisibleInMenu( true );
}
menu.addAction( action );

tableOfContents[ action ] = *i;

break;
auto dictionary = dictionaryGroup->getDictionaryById( i->toUtf8().data() );
if ( dictionary ) {
QAction * action = nullptr;
if ( refsAdded == cfg.preferences.maxDictionaryRefsInContextMenu ) {
// Enough! Or the menu would become too large.
maxDictionaryRefsAction = new QAction( ".........", &menu );
action = maxDictionaryRefsAction;
maxDictionaryRefsReached = true;
}
else {
action = new QAction( dictionary->getIcon(), QString::fromUtf8( dictionary->getName().c_str() ), &menu );
// Force icons in menu on all platforms,
// since without them it will be much harder
// to find things.
action->setIconVisibleInMenu( true );
}
menu.addAction( action );

tableOfContents[ action ] = *i;
}

if ( maxDictionaryRefsReached )
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/ui/articleview.hh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "articlewebview.hh"
#include "ui/searchpanel.hh"
#include "ui/ftssearchpanel.hh"
#include "dictionary_group.hh"

class ResourceToSaveHandler;
class ArticleViewAgent;
Expand All @@ -35,8 +36,7 @@ class ArticleView: public QWidget

ArticleNetworkAccessManager & articleNetMgr;
AudioPlayerPtr const & audioPlayer;
std::vector< sptr< Dictionary::Class > > const & allDictionaries;
Instances::Groups const & groups;
std::unique_ptr< DictionaryGroup > dictionaryGroup;
bool popupView;
Config::Class const & cfg;
QWebChannel * channel;
Expand Down
Loading