mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-27 19:24:08 +00:00
Merge pull request #35 from ngn999/bugfix/Macbook_scroll_issue
disable macOS trackpad zoom; fix a deadlock
This commit is contained in:
commit
228d7001e5
256
articleview.cc
256
articleview.cc
|
@ -2,6 +2,7 @@
|
|||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "articleview.hh"
|
||||
#include "QtCore/qvariant.h"
|
||||
#include "folding.hh"
|
||||
#include "fulltextsearch.hh"
|
||||
#include "gddebug.hh"
|
||||
|
@ -179,27 +180,6 @@ public:
|
|||
void ArticleView::emitJavascriptFinished(){
|
||||
emit notifyJavascriptFinished();
|
||||
}
|
||||
//in webengine,javascript has been executed in async mode ,for simpility,use EventLoop to simulate sync execution.
|
||||
//a better solution would be to replace it with callback etc.
|
||||
QString ArticleView::runJavaScriptSync(QWebEnginePage* frame, const QString& variable)
|
||||
{
|
||||
qDebug("%s", QString("runJavascriptScriptSync:%1").arg(variable).toLatin1().data());
|
||||
|
||||
QString result;
|
||||
QSharedPointer<QEventLoop> loop = QSharedPointer<QEventLoop>(new QEventLoop());
|
||||
QTimer::singleShot(1000, loop.data(), &QEventLoop::quit);
|
||||
frame->runJavaScript(variable, [=,&result](const QVariant &v)
|
||||
{
|
||||
if(loop->isRunning()){
|
||||
if(v.isValid())
|
||||
result = v.toString();
|
||||
loop->quit();
|
||||
}
|
||||
});
|
||||
|
||||
loop->exec();
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -700,13 +680,13 @@ void ArticleView::selectCurrentArticle()
|
|||
QString( "gdSelectArticle( '%1' );var elem=document.getElementById('%2'); if(elem!=undefined){elem.scrollIntoView(true);}" ).arg( getActiveArticleId() ,getCurrentArticle()) );
|
||||
}
|
||||
|
||||
bool ArticleView::isFramedArticle( QString const & ca )
|
||||
void ArticleView::isFramedArticle( QString const & ca, const std::function< void( bool ) > & callback )
|
||||
{
|
||||
if ( ca.isEmpty() )
|
||||
return false;
|
||||
if( ca.isEmpty() )
|
||||
callback( false );
|
||||
|
||||
QString result=runJavaScriptSync( ui.definition->page(), QString( "!!document.getElementById('gdexpandframe-%1');" ).arg( ca.mid( 7 ) ) );
|
||||
return result=="true";
|
||||
ui.definition->page()->runJavaScript( QString( "!!document.getElementById('gdexpandframe-%1');" ).arg( ca.mid( 7 ) ),
|
||||
[ callback ]( const QVariant & res ) { callback( res.toBool() ); } );
|
||||
}
|
||||
|
||||
bool ArticleView::isExternalLink( QUrl const & url )
|
||||
|
@ -718,39 +698,41 @@ void ArticleView::tryMangleWebsiteClickedUrl( QUrl & url, Contexts & contexts )
|
|||
{
|
||||
// Don't try mangling audio urls, even if they are from the framed websites
|
||||
|
||||
if( ( url.scheme() == "http" || url.scheme() == "https" )
|
||||
&& ! Dictionary::WebMultimediaDownload::isAudioUrl( url ) )
|
||||
if( ( url.scheme() == "http" || url.scheme() == "https" ) && !Dictionary::WebMultimediaDownload::isAudioUrl( url ) )
|
||||
{
|
||||
// Maybe a link inside a website was clicked?
|
||||
|
||||
QString ca = getCurrentArticle();
|
||||
isFramedArticle( ca,
|
||||
[ =, &url ]( bool framed )
|
||||
{
|
||||
if( framed )
|
||||
{
|
||||
// QVariant result = runJavaScriptSync( ui.definition->page(), "gdLastUrlText" );
|
||||
QVariant result;
|
||||
|
||||
if ( isFramedArticle( ca ) )
|
||||
{
|
||||
//QVariant result = runJavaScriptSync( ui.definition->page(), "gdLastUrlText" );
|
||||
QVariant result ;
|
||||
if( result.type() == QVariant::String )
|
||||
{
|
||||
// Looks this way
|
||||
contexts[ dictionaryIdFromScrollTo( ca ) ] = QString::fromLatin1( url.toEncoded() );
|
||||
|
||||
if ( result.type() == QVariant::String )
|
||||
{
|
||||
// Looks this way
|
||||
contexts[ dictionaryIdFromScrollTo( ca ) ] = QString::fromLatin1( url.toEncoded() );
|
||||
QUrl target;
|
||||
|
||||
QUrl target;
|
||||
QString queryWord = result.toString();
|
||||
|
||||
QString queryWord = result.toString();
|
||||
// Empty requests are treated as no request, so we work this around by
|
||||
// adding a space.
|
||||
if( queryWord.isEmpty() )
|
||||
queryWord = " ";
|
||||
|
||||
// Empty requests are treated as no request, so we work this around by
|
||||
// adding a space.
|
||||
if ( queryWord.isEmpty() )
|
||||
queryWord = " ";
|
||||
target.setScheme( "gdlookup" );
|
||||
target.setHost( "localhost" );
|
||||
target.setPath( "/" + queryWord );
|
||||
|
||||
target.setScheme( "gdlookup" );
|
||||
target.setHost( "localhost" );
|
||||
target.setPath( "/" + queryWord );
|
||||
|
||||
url = target;
|
||||
}
|
||||
}
|
||||
url = target;
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -829,6 +811,17 @@ bool ArticleView::handleF3( QObject * /*obj*/, QEvent * ev )
|
|||
|
||||
bool ArticleView::eventFilter( QObject * obj, QEvent * ev )
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
|
||||
if( ev->type() == QEvent::NativeGesture )
|
||||
{
|
||||
qDebug() << "it's a Native Gesture!";
|
||||
// handle Qt::ZoomNativeGesture Qt::SmartZoomNativeGesture here
|
||||
// ignore swipe left/right.
|
||||
// QWebEngine can handle Qt::SmartZoomNativeGesture.
|
||||
}
|
||||
|
||||
#else
|
||||
if( ev->type() == QEvent::Gesture )
|
||||
{
|
||||
Gestures::GestureResult result;
|
||||
|
@ -873,6 +866,7 @@ bool ArticleView::eventFilter( QObject * obj, QEvent * ev )
|
|||
|
||||
return handled;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( ev->type() == QEvent::MouseMove )
|
||||
{
|
||||
|
@ -1644,12 +1638,16 @@ void ArticleView::forward()
|
|||
ui.definition->forward();
|
||||
}
|
||||
|
||||
bool ArticleView::hasSound()
|
||||
void ArticleView::hasSound( const std::function< void( bool ) > & callback )
|
||||
{
|
||||
QVariant v = runJavaScriptSync( ui.definition->page(),"gdAudioLinks.first" );
|
||||
if ( v.type() == QVariant::String )
|
||||
return !v.toString().isEmpty();
|
||||
return false;
|
||||
ui.definition->page()->runJavaScript( "gdAudioLinks.first",
|
||||
[ callback ]( const QVariant & v )
|
||||
{
|
||||
bool has = false;
|
||||
if( v.type() == QVariant::String )
|
||||
has = !v.toString().isEmpty();
|
||||
callback( has );
|
||||
} );
|
||||
}
|
||||
|
||||
//use webengine javascript to playsound
|
||||
|
@ -1661,26 +1659,24 @@ void ArticleView::playSound()
|
|||
" } "
|
||||
" return link;})(); ";
|
||||
|
||||
QString soundScript = runJavaScriptSync(ui.definition->page(), variable);
|
||||
if (!soundScript.isEmpty())
|
||||
openLink(QUrl::fromEncoded(soundScript.toUtf8()), ui.definition->url());
|
||||
ui.definition->page()->runJavaScript(variable,[this](const QVariant & result){
|
||||
if (result.type() == QVariant::String) {
|
||||
QString soundScript = result.toString();
|
||||
if (!soundScript.isEmpty())
|
||||
openLink(QUrl::fromEncoded(soundScript.toUtf8()), ui.definition->url());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// use eventloop to turn the async callback to sync execution.
|
||||
QString ArticleView::toHtml()
|
||||
void ArticleView::toHtml( const std::function< void( QString & ) > & callback )
|
||||
{
|
||||
QString result;
|
||||
QSharedPointer<QEventLoop> loop = QSharedPointer<QEventLoop>(new QEventLoop());
|
||||
QTimer::singleShot(1000, loop.data(), &QEventLoop::quit);
|
||||
|
||||
ui.definition->page()->toHtml([loop, &result](const QString &content) {
|
||||
if (loop->isRunning()) {
|
||||
result = content;
|
||||
loop->quit();
|
||||
}
|
||||
});
|
||||
loop->exec();
|
||||
return result;
|
||||
ui.definition->page()->toHtml(
|
||||
[ = ]( const QString & content )
|
||||
{
|
||||
QString html = content;
|
||||
callback( html );
|
||||
} );
|
||||
}
|
||||
|
||||
void ArticleView::setHtml(const QString& content,const QUrl& baseUrl){
|
||||
|
@ -2507,10 +2503,9 @@ void ArticleView::highlightFTSResults()
|
|||
else
|
||||
regexp.setPattern( regString );
|
||||
|
||||
QRegularExpression::PatternOptions patternOptions = QRegularExpression::DotMatchesEverythingOption
|
||||
| QRegularExpression::UseUnicodePropertiesOption
|
||||
| QRegularExpression::MultilineOption
|
||||
| QRegularExpression::InvertedGreedinessOption;
|
||||
QRegularExpression::PatternOptions patternOptions =
|
||||
QRegularExpression::DotMatchesEverythingOption | QRegularExpression::UseUnicodePropertiesOption |
|
||||
QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption;
|
||||
if( !Utils::Url::hasQueryItem( url, "matchcase" ) )
|
||||
patternOptions |= QRegularExpression::CaseInsensitiveOption;
|
||||
regexp.setPatternOptions( patternOptions );
|
||||
|
@ -2518,84 +2513,69 @@ void ArticleView::highlightFTSResults()
|
|||
if( regexp.pattern().isEmpty() || !regexp.isValid() )
|
||||
return;
|
||||
|
||||
|
||||
sptr< AccentMarkHandler > marksHandler = ignoreDiacritics ?
|
||||
new DiacriticsHandler : new AccentMarkHandler;
|
||||
sptr< AccentMarkHandler > marksHandler = ignoreDiacritics ? new DiacriticsHandler : new AccentMarkHandler;
|
||||
|
||||
// Clear any current selection
|
||||
if ( ui.definition->selectedText().size() )
|
||||
if( ui.definition->selectedText().size() )
|
||||
{
|
||||
ui.definition->page()->
|
||||
runJavaScript( "window.getSelection().removeAllRanges();_=0;" );
|
||||
ui.definition->page()->runJavaScript( "window.getSelection().removeAllRanges();_=0;" );
|
||||
}
|
||||
|
||||
QString pageText = getWebPageTextSync(ui.definition->page());
|
||||
marksHandler->setText( pageText );
|
||||
|
||||
QRegularExpressionMatchIterator it = regexp.globalMatch( marksHandler->normalizedText() );
|
||||
while( it.hasNext() )
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
|
||||
// Mirror pos and matched length to original string
|
||||
int pos = match.capturedStart();
|
||||
int spos = marksHandler->mirrorPosition( pos );
|
||||
int matched = marksHandler->mirrorPosition( pos + match.capturedLength() ) - spos;
|
||||
|
||||
// Add mark pos (if presented)
|
||||
while( spos + matched < pageText.length()
|
||||
&& pageText[ spos + matched ].category() == QChar::Mark_NonSpacing )
|
||||
matched++;
|
||||
|
||||
if( matched > FTS::MaxMatchLengthForHighlightResults )
|
||||
ui.definition->page()->toPlainText(
|
||||
[ & ]( const QString pageText )
|
||||
{
|
||||
gdWarning( "ArticleView::highlightFTSResults(): Too long match - skipped (matched length %i, allowed %i)",
|
||||
match.capturedLength(), FTS::MaxMatchLengthForHighlightResults );
|
||||
}
|
||||
else
|
||||
allMatches.append( pageText.mid( spos, matched ) );
|
||||
}
|
||||
marksHandler->setText( pageText );
|
||||
|
||||
ftsSearchMatchCase = Utils::Url::hasQueryItem( url, "matchcase" );
|
||||
QRegularExpressionMatchIterator it = regexp.globalMatch( marksHandler->normalizedText() );
|
||||
while( it.hasNext() )
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
|
||||
QWebEnginePage::FindFlags flags ( 0 );
|
||||
// Mirror pos and matched length to original string
|
||||
int pos = match.capturedStart();
|
||||
int spos = marksHandler->mirrorPosition( pos );
|
||||
int matched = marksHandler->mirrorPosition( pos + match.capturedLength() ) - spos;
|
||||
|
||||
if( ftsSearchMatchCase )
|
||||
flags |= QWebEnginePage::FindCaseSensitively;
|
||||
// Add mark pos (if presented)
|
||||
while( spos + matched < pageText.length() && pageText[ spos + matched ].category() == QChar::Mark_NonSpacing )
|
||||
matched++;
|
||||
|
||||
for( int x = 0; x < allMatches.size(); x++ )
|
||||
ui.definition->findText( allMatches.at( x ), flags );
|
||||
if( matched > FTS::MaxMatchLengthForHighlightResults )
|
||||
{
|
||||
gdWarning( "ArticleView::highlightFTSResults(): Too long match - skipped (matched length %i, allowed %i)",
|
||||
match.capturedLength(),
|
||||
FTS::MaxMatchLengthForHighlightResults );
|
||||
}
|
||||
else
|
||||
allMatches.append( pageText.mid( spos, matched ) );
|
||||
}
|
||||
|
||||
if( !allMatches.isEmpty() )
|
||||
{
|
||||
ui.definition->findText( allMatches.at( 0 ), flags );
|
||||
//if( ui.definition->findText( allMatches.at( 0 ), flags ) )
|
||||
{
|
||||
ui.definition->page()->
|
||||
runJavaScript( QString( "%1=window.getSelection().getRangeAt(0);_=0;" )
|
||||
.arg( rangeVarName ) );
|
||||
}
|
||||
}
|
||||
ftsSearchMatchCase = Utils::Url::hasQueryItem( url, "matchcase" );
|
||||
|
||||
ui.ftsSearchFrame->show();
|
||||
ui.ftsSearchPrevious->setEnabled( false );
|
||||
ui.ftsSearchNext->setEnabled( allMatches.size()>1 );
|
||||
QWebEnginePage::FindFlags flags( 0 );
|
||||
|
||||
ftsSearchIsOpened = true;
|
||||
}
|
||||
if( ftsSearchMatchCase )
|
||||
flags |= QWebEnginePage::FindCaseSensitively;
|
||||
|
||||
QString ArticleView::getWebPageTextSync(QWebEnginePage * page){
|
||||
QString planText;
|
||||
QSharedPointer<QEventLoop> loop = QSharedPointer<QEventLoop>(new QEventLoop());
|
||||
QTimer::singleShot(1000, loop.data(), &QEventLoop::quit);
|
||||
page->toPlainText([&](const QString &result)
|
||||
{
|
||||
if(loop->isRunning()){
|
||||
planText = result;
|
||||
loop->quit();
|
||||
} });
|
||||
loop->exec();
|
||||
return planText;
|
||||
for( int x = 0; x < allMatches.size(); x++ )
|
||||
ui.definition->findText( allMatches.at( x ), flags );
|
||||
|
||||
if( !allMatches.isEmpty() )
|
||||
{
|
||||
ui.definition->findText( allMatches.at( 0 ), flags );
|
||||
// if( ui.definition->findText( allMatches.at( 0 ), flags ) )
|
||||
{
|
||||
ui.definition->page()->runJavaScript(
|
||||
QString( "%1=window.getSelection().getRangeAt(0);_=0;" ).arg( rangeVarName ) );
|
||||
}
|
||||
}
|
||||
|
||||
ui.ftsSearchFrame->show();
|
||||
ui.ftsSearchPrevious->setEnabled( false );
|
||||
ui.ftsSearchNext->setEnabled( allMatches.size() > 1 );
|
||||
|
||||
ftsSearchIsOpened = true;
|
||||
} );
|
||||
}
|
||||
|
||||
void ArticleView::setActiveDictIds(ActiveDictIds ad) {
|
||||
|
|
|
@ -112,8 +112,6 @@ public:
|
|||
/// Returns "gdfrom-" + dictionaryId.
|
||||
static QString scrollToFromDictionaryId( QString const & dictionaryId );
|
||||
|
||||
QString runJavaScriptSync(QWebEnginePage* frame, const QString& variable);
|
||||
|
||||
void emitJavascriptFinished();
|
||||
|
||||
/// Shows the definition of the given word with the given group.
|
||||
|
@ -158,8 +156,6 @@ public:
|
|||
/// Called when preference changes
|
||||
void setSelectionBySingleClick( bool set );
|
||||
|
||||
QString getWebPageTextSync(QWebEnginePage * page);
|
||||
|
||||
public slots:
|
||||
|
||||
/// Goes back in history
|
||||
|
@ -179,7 +175,7 @@ public:
|
|||
{ ui.definition->reload(); }
|
||||
|
||||
/// Returns true if there's an audio reference on the page, false otherwise.
|
||||
bool hasSound();
|
||||
void hasSound( const std::function< void( bool has ) > & callback );
|
||||
|
||||
/// Plays the first audio reference on the page, if any.
|
||||
void playSound();
|
||||
|
@ -195,7 +191,7 @@ public:
|
|||
}
|
||||
|
||||
/// Returns current article's text in .html format
|
||||
QString toHtml();
|
||||
void toHtml( const std::function< void( QString & ) > & callback );
|
||||
|
||||
void setHtml(const QString& content, const QUrl& baseUrl);
|
||||
void setContent(const QByteArray &data, const QString &mimeType = QString(), const QUrl &baseUrl = QUrl());
|
||||
|
@ -369,7 +365,7 @@ private:
|
|||
|
||||
/// Checks if the given article in form of "gdfrom-xxx" is inside a "website"
|
||||
/// frame.
|
||||
bool isFramedArticle( QString const & );
|
||||
void isFramedArticle( QString const & article, const std::function< void( bool framed ) > & callback );
|
||||
|
||||
/// Checks if the given link is to be opened externally, as opposed to opening
|
||||
/// it in-place.
|
||||
|
|
184
mainwindow.cc
184
mainwindow.cc
|
@ -174,8 +174,9 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
|
|||
articleMaker.setCollapseParameters( cfg.preferences.collapseBigArticles, cfg.preferences.articleSizeLimit );
|
||||
|
||||
// Set own gesture recognizers
|
||||
#ifndef Q_OS_MAC
|
||||
Gestures::registerRecognizers();
|
||||
|
||||
#endif
|
||||
// use our own, custom statusbar
|
||||
setStatusBar(0);
|
||||
mainStatusBar = new MainStatusBar( this );
|
||||
|
@ -2033,10 +2034,14 @@ void MainWindow::updateBackForwardButtons()
|
|||
|
||||
void MainWindow::updatePronounceAvailability()
|
||||
{
|
||||
bool pronounceEnabled = ui.tabWidget->count() > 0 &&
|
||||
getCurrentArticleView()->hasSound();
|
||||
|
||||
navPronounce->setEnabled( pronounceEnabled );
|
||||
if (ui.tabWidget->count() > 0) {
|
||||
getCurrentArticleView()->hasSound([this](bool has) {
|
||||
navPronounce->setEnabled( has );
|
||||
});
|
||||
}
|
||||
else {
|
||||
navPronounce->setEnabled( false );
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::editDictionaries( unsigned editDictionaryGroup )
|
||||
|
@ -3405,7 +3410,7 @@ static void filterAndCollectResources( QString & html, QRegExp & rx, const QStri
|
|||
|
||||
void MainWindow::on_saveArticle_triggered()
|
||||
{
|
||||
ArticleView *view = getCurrentArticleView();
|
||||
ArticleView * view = getCurrentArticleView();
|
||||
|
||||
QString fileName = view->getTitle().simplified();
|
||||
|
||||
|
@ -3416,12 +3421,12 @@ void MainWindow::on_saveArticle_triggered()
|
|||
fileName += ".html";
|
||||
QString savePath;
|
||||
|
||||
if ( cfg.articleSavePath.isEmpty() )
|
||||
if( cfg.articleSavePath.isEmpty() )
|
||||
savePath = QDir::homePath();
|
||||
else
|
||||
{
|
||||
savePath = QDir::fromNativeSeparators( cfg.articleSavePath );
|
||||
if ( !QDir( savePath ).exists() )
|
||||
if( !QDir( savePath ).exists() )
|
||||
savePath = QDir::homePath();
|
||||
}
|
||||
|
||||
|
@ -3432,97 +3437,102 @@ void MainWindow::on_saveArticle_triggered()
|
|||
filters.push_back( tr( "Article, HTML Only (*.html)" ) );
|
||||
|
||||
fileName = savePath + "/" + fileName;
|
||||
fileName = QFileDialog::getSaveFileName( this, tr( "Save Article As" ),
|
||||
fileName = QFileDialog::getSaveFileName( this,
|
||||
tr( "Save Article As" ),
|
||||
fileName,
|
||||
filters.join( ";;" ),
|
||||
&selectedFilter, options );
|
||||
&selectedFilter,
|
||||
options );
|
||||
|
||||
bool complete = ( selectedFilter == filters[ 0 ] );
|
||||
|
||||
if ( !fileName.isEmpty() )
|
||||
{
|
||||
if( fileName.isEmpty() )
|
||||
return;
|
||||
|
||||
QFile file( fileName );
|
||||
|
||||
if ( !file.open( QIODevice::WriteOnly ) )
|
||||
view->toHtml(
|
||||
[ = ]( QString & html ) mutable
|
||||
{
|
||||
QMessageBox::critical( this, tr( "Error" ),
|
||||
tr( "Can't save article: %1" ).arg( file.errorString() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QString html = view->toHtml();
|
||||
QFileInfo fi( fileName );
|
||||
cfg.articleSavePath = QDir::toNativeSeparators( fi.absoluteDir().absolutePath() );
|
||||
|
||||
// Convert internal links
|
||||
|
||||
QRegExp rx3( "href=\"(bword:|gdlookup://localhost/)([^\"]+)\"" );
|
||||
int pos = 0;
|
||||
QRegularExpression anchorRx( "(g[0-9a-f]{32}_)[0-9a-f]+_" );
|
||||
while ( ( pos = rx3.indexIn( html, pos ) ) != -1 )
|
||||
QFile file( fileName );
|
||||
if( !file.open( QIODevice::WriteOnly ) )
|
||||
{
|
||||
QString name = QUrl::fromPercentEncoding( rx3.cap( 2 ).simplified().toLatin1() );
|
||||
QString anchor;
|
||||
name.replace( "?gdanchor=", "#" );
|
||||
int n = name.indexOf( '#' );
|
||||
if( n > 0 )
|
||||
{
|
||||
anchor = name.mid( n );
|
||||
name.truncate( n );
|
||||
anchor.replace( anchorRx, "\\1" ); // MDict anchors
|
||||
}
|
||||
name.replace( rxName, "_" );
|
||||
name = QString( "href=\"" ) + QUrl::toPercentEncoding( name ) + ".html" + anchor + "\"";
|
||||
html.replace( pos, rx3.cap().length(), name );
|
||||
pos += name.length();
|
||||
}
|
||||
|
||||
// MDict anchors
|
||||
QRegularExpression anchorLinkRe( "(<\\s*a\\s+[^>]*\\b(?:name|id)\\b\\s*=\\s*[\"']*g[0-9a-f]{32}_)([0-9a-f]+_)(?=[^\"'])", QRegularExpression::PatternOption::CaseInsensitiveOption );
|
||||
html.replace( anchorLinkRe, "\\1" );
|
||||
|
||||
if ( complete )
|
||||
{
|
||||
QString folder = fi.absoluteDir().absolutePath() + "/" + fi.baseName() + "_files";
|
||||
QRegExp rx1( "\"((?:bres|gico|gdau|qrcx|gdvideo)://[^\"]+)\"" );
|
||||
QRegExp rx2( "'((?:bres|gico|gdau|qrcx|gdvideo)://[^']+)'" );
|
||||
set< QByteArray > resourceIncluded;
|
||||
vector< pair< QUrl, QString > > downloadResources;
|
||||
|
||||
filterAndCollectResources( html, rx1, "\"", folder, resourceIncluded, downloadResources );
|
||||
filterAndCollectResources( html, rx2, "'", folder, resourceIncluded, downloadResources );
|
||||
|
||||
ArticleSaveProgressDialog * progressDialog = new ArticleSaveProgressDialog( this );
|
||||
// reserve '1' for saving main html file
|
||||
int maxVal = 1;
|
||||
|
||||
// Pull and save resources to files
|
||||
for ( vector< pair< QUrl, QString > >::const_iterator i = downloadResources.begin();
|
||||
i != downloadResources.end(); ++i )
|
||||
{
|
||||
ResourceToSaveHandler * handler = view->saveResource( i->first, i->second );
|
||||
if( !handler->isEmpty() )
|
||||
{
|
||||
maxVal += 1;
|
||||
connect( handler, SIGNAL( done() ), progressDialog, SLOT( perform() ) );
|
||||
}
|
||||
}
|
||||
|
||||
progressDialog->setLabelText( tr("Saving article...") );
|
||||
progressDialog->setRange( 0, maxVal );
|
||||
progressDialog->setValue( 0 );
|
||||
progressDialog->show();
|
||||
|
||||
file.write( html.toUtf8() );
|
||||
progressDialog->setValue( 1 );
|
||||
QMessageBox::critical( this, tr( "Error" ), tr( "Can't save article: %1" ).arg( file.errorString() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
file.write( html.toUtf8() );
|
||||
QFileInfo fi( fileName );
|
||||
cfg.articleSavePath = QDir::toNativeSeparators( fi.absoluteDir().absolutePath() );
|
||||
|
||||
// Convert internal links
|
||||
|
||||
QRegExp rx3( "href=\"(bword:|gdlookup://localhost/)([^\"]+)\"" );
|
||||
int pos = 0;
|
||||
QRegularExpression anchorRx( "(g[0-9a-f]{32}_)[0-9a-f]+_" );
|
||||
while( ( pos = rx3.indexIn( html, pos ) ) != -1 )
|
||||
{
|
||||
QString name = QUrl::fromPercentEncoding( rx3.cap( 2 ).simplified().toLatin1() );
|
||||
QString anchor;
|
||||
name.replace( "?gdanchor=", "#" );
|
||||
int n = name.indexOf( '#' );
|
||||
if( n > 0 )
|
||||
{
|
||||
anchor = name.mid( n );
|
||||
name.truncate( n );
|
||||
anchor.replace( anchorRx, "\\1" ); // MDict anchors
|
||||
}
|
||||
name.replace( rxName, "_" );
|
||||
name = QString( "href=\"" ) + QUrl::toPercentEncoding( name ) + ".html" + anchor + "\"";
|
||||
html.replace( pos, rx3.cap().length(), name );
|
||||
pos += name.length();
|
||||
}
|
||||
|
||||
// MDict anchors
|
||||
QRegularExpression anchorLinkRe(
|
||||
"(<\\s*a\\s+[^>]*\\b(?:name|id)\\b\\s*=\\s*[\"']*g[0-9a-f]{32}_)([0-9a-f]+_)(?=[^\"'])",
|
||||
QRegularExpression::PatternOption::CaseInsensitiveOption );
|
||||
html.replace( anchorLinkRe, "\\1" );
|
||||
|
||||
if( complete )
|
||||
{
|
||||
QString folder = fi.absoluteDir().absolutePath() + "/" + fi.baseName() + "_files";
|
||||
QRegExp rx1( "\"((?:bres|gico|gdau|qrcx|gdvideo)://[^\"]+)\"" );
|
||||
QRegExp rx2( "'((?:bres|gico|gdau|qrcx|gdvideo)://[^']+)'" );
|
||||
set< QByteArray > resourceIncluded;
|
||||
vector< pair< QUrl, QString > > downloadResources;
|
||||
|
||||
filterAndCollectResources( html, rx1, "\"", folder, resourceIncluded, downloadResources );
|
||||
filterAndCollectResources( html, rx2, "'", folder, resourceIncluded, downloadResources );
|
||||
|
||||
ArticleSaveProgressDialog * progressDialog = new ArticleSaveProgressDialog( this );
|
||||
// reserve '1' for saving main html file
|
||||
int maxVal = 1;
|
||||
|
||||
// Pull and save resources to files
|
||||
for( vector< pair< QUrl, QString > >::const_iterator i = downloadResources.begin();
|
||||
i != downloadResources.end();
|
||||
++i )
|
||||
{
|
||||
ResourceToSaveHandler * handler = view->saveResource( i->first, i->second );
|
||||
if( !handler->isEmpty() )
|
||||
{
|
||||
maxVal += 1;
|
||||
connect( handler, SIGNAL( done() ), progressDialog, SLOT( perform() ) );
|
||||
}
|
||||
}
|
||||
|
||||
progressDialog->setLabelText( tr( "Saving article..." ) );
|
||||
progressDialog->setRange( 0, maxVal );
|
||||
progressDialog->setValue( 0 );
|
||||
progressDialog->show();
|
||||
|
||||
file.write( html.toUtf8() );
|
||||
progressDialog->perform();
|
||||
}
|
||||
else
|
||||
{
|
||||
file.write( html.toUtf8() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void MainWindow::on_rescanFiles_triggered()
|
||||
|
|
6
mutex.hh
6
mutex.hh
|
@ -4,16 +4,16 @@
|
|||
#ifndef __MUTEX_HH_INCLUDED__
|
||||
#define __MUTEX_HH_INCLUDED__
|
||||
|
||||
#include <QMutex>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
/// This provides a mutex class. As you can see, it's just a Qt one, but it
|
||||
/// does provide the Lock class which doesn't seem to exist in Qt, and it does
|
||||
/// provide some abstraction for dictionaries in case they are to be ported
|
||||
/// away from Qt.
|
||||
class Mutex: public QMutex
|
||||
class Mutex : public QRecursiveMutex
|
||||
{
|
||||
public:
|
||||
Mutex() : QMutex( )
|
||||
Mutex() : QRecursiveMutex()
|
||||
{}
|
||||
~Mutex()
|
||||
{}
|
||||
|
|
|
@ -1141,7 +1141,11 @@ void ScanPopup::altModePoll()
|
|||
|
||||
void ScanPopup::pageLoaded( ArticleView * )
|
||||
{
|
||||
ui.pronounceButton->setVisible( definition->hasSound() );
|
||||
|
||||
|
||||
definition->hasSound([this](bool has){
|
||||
ui.pronounceButton->setVisible( has );
|
||||
});
|
||||
|
||||
updateBackForwardButtons();
|
||||
|
||||
|
|
Loading…
Reference in a new issue