fix: highlight fts search result (#792)

* fix: highlight fts search result

* 🎨 apply clang-format changes

* remove accent/diacritic handler used in fts search

* header file adjustment

* fix: qt5.15 compile

* fix: code smell

use const reference variable

* fix: code smell

* fix: code smell

* fix: code smell

* fix: code smell

* 🎨 apply clang-format changes

* fix: code smells

🎨 apply clang-format changes

* opt: fix code smells

remove unused parameters from function `ftsIndexIsOldOrBad`

🎨 apply clang-format changes

---------

Co-authored-by: xiaoyifang <xiaoyifang@users.noreply.github.com>
This commit is contained in:
xiaoyifang 2023-06-03 08:29:19 +08:00 committed by GitHub
parent e37e66b330
commit 5899aa7c94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 196 additions and 415 deletions

View file

@ -318,8 +318,7 @@ AardDictionary::AardDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -575,11 +574,11 @@ QString const& AardDictionary::getDescription()
void AardDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )
if ( haveFTSIndex() )
return;
if( ensureInitDone().size() )

View file

@ -286,8 +286,8 @@ namespace
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -451,8 +451,8 @@ namespace
void BglDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )

View file

@ -180,8 +180,7 @@ DictdDictionary::DictdDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -460,11 +459,11 @@ QString const& DictdDictionary::getDescription()
void DictdDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )
if ( haveFTSIndex() )
return;
if( ensureInitDone().size() )

View file

@ -287,8 +287,7 @@ DslDictionary::DslDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
// Read the dictionary name
@ -1216,12 +1215,12 @@ QString DslDictionary::getMainFilename() { return getDictionaryFilenames()[ 0 ].
void DslDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )
if ( haveFTSIndex() )
return;
if( ensureInitDone().size() )

View file

@ -258,8 +258,7 @@ EpwingDictionary::EpwingDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -461,8 +460,8 @@ QString const& EpwingDictionary::getDescription()
void EpwingDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();

View file

@ -499,8 +499,7 @@ GlsDictionary::GlsDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -570,8 +569,8 @@ QString GlsDictionary::getMainFilename() { return getDictionaryFilenames()[ 0 ].
void GlsDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )

View file

@ -336,8 +336,7 @@ MdxDictionary::MdxDictionary( string const & id, string const & indexFile, vecto
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
cacheDirName = QDir::tempPath() + QDir::separator()
@ -461,8 +460,8 @@ void MdxDictionary::doDeferredInit()
void MdxDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )

View file

@ -220,8 +220,7 @@ SdictDictionary::SdictDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -405,11 +404,11 @@ void SdictDictionary::loadArticle( uint32_t address,
void SdictDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )
if ( haveFTSIndex() )
return;
if( ensureInitDone().size() )

View file

@ -707,8 +707,7 @@ SlobDictionary::SlobDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
texCgiPath = Config::getProgramDataDir() + "/mimetex.cgi";
@ -1112,7 +1111,7 @@ void SlobDictionary::sortArticlesOffsetsForFTS( QVector< uint32_t > & offsets, Q
void SlobDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if ( haveFTSIndex() )

View file

@ -275,8 +275,7 @@ StardictDictionary::StardictDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -1155,11 +1154,11 @@ QString StardictDictionary::getMainFilename() { return getDictionaryFilenames()[
void StardictDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )
if ( haveFTSIndex() )
return;
if( ensureInitDone().size() )

View file

@ -311,8 +311,7 @@ XdxfDictionary::XdxfDictionary( string const & id,
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -384,11 +383,11 @@ QString XdxfDictionary::getMainFilename() { return getDictionaryFilenames()[ 0 ]
void XdxfDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )
if ( haveFTSIndex() )
return;
if( ensureInitDone().size() )

View file

@ -266,8 +266,7 @@ ZimDictionary::ZimDictionary( string const & id, string const & indexFile, vecto
ftsIdxName = indexFile + Dictionary::getFtsSuffix();
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
if ( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName ) && !FtsHelpers::ftsIndexIsOldOrBad( this ) )
FTS_index_completed.ref();
}
@ -473,8 +472,8 @@ QString const& ZimDictionary::getDescription()
void ZimDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|| FtsHelpers::ftsIndexIsOldOrBad( this ) ) )
FTS_index_completed.ref();
if( haveFTSIndex() )

View file

@ -31,18 +31,16 @@ namespace FtsHelpers
// finished reversed dehsinif
const static std::string finish_mark = std::string( "dehsinif" );
bool ftsIndexIsOldOrBad( string const & indexFile,
BtreeIndexing::BtreeDictionary * dict )
bool ftsIndexIsOldOrBad( BtreeIndexing::BtreeDictionary * dict )
{
try
{
try {
Xapian::WritableDatabase db( dict->ftsIndexName() );
auto docid = db.get_lastdocid();
auto document = db.get_document(docid);
auto docid = db.get_lastdocid();
auto document = db.get_document( docid );
qDebug()<<document.get_data().c_str();
qDebug() << document.get_data().c_str();
//use a special document to mark the end of the index.
return document.get_data()!=finish_mark;
return document.get_data() != finish_mark;
}
catch( Xapian::Error & e )
{
@ -55,7 +53,6 @@ bool ftsIndexIsOldOrBad( string const & indexFile,
{
return true;
}
return false;
}
static QString makeHiliteRegExpString( QStringList const & words,

View file

@ -21,8 +21,7 @@
namespace FtsHelpers
{
bool ftsIndexIsOldOrBad( std::string const & indexFile,
BtreeIndexing::BtreeDictionary * dict );
bool ftsIndexIsOldOrBad( BtreeIndexing::BtreeDictionary * dict );
bool parseSearchString( QString const & str, QStringList & IndexWords,
QStringList & searchWords,

View file

@ -4,7 +4,6 @@
#include "articleview.hh"
#include "dict/programs.hh"
#include "folding.hh"
#include "fulltextsearch.hh"
#include "gddebug.hh"
#include "gestures.hh"
#include "globalbroadcaster.hh"
@ -30,6 +29,7 @@
#include <QWebEngineScriptCollection>
#include <QWebEngineSettings>
#include <map>
#include <QApplication>
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(6,0,0))
#include <QWebEngineContextMenuData>
@ -37,7 +37,8 @@
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
#include <QtCore5Compat/QRegExp>
#include <QWebEngineContextMenuRequest>
#include <QWebEngineFindTextResult>
#include <QWebEngineFindTextResult>
#include <utility>
#endif
#ifdef Q_OS_WIN32
#include <windows.h>
@ -47,122 +48,6 @@
using std::map;
using std::list;
/// AccentMarkHandler class
///
/// Remove accent marks from text
/// and mirror position in normalized text to original text
class AccentMarkHandler
{
protected:
QString normalizedString;
QVector< int > accentMarkPos;
public:
AccentMarkHandler() = default;
virtual ~AccentMarkHandler() = default;
static QChar accentMark()
{ return QChar( 0x301 ); }
/// Create text without accent marks
/// and store mark positions
virtual void setText( QString const & baseString )
{
accentMarkPos.clear();
normalizedString.clear();
int pos = 0;
QChar mark = accentMark();
for ( auto const & str : baseString ) {
if ( str == mark ) {
accentMarkPos.append( pos );
continue;
}
normalizedString.append( str );
pos++;
}
}
/// Return text without accent marks
QString const & normalizedText() const
{ return normalizedString; }
/// Convert position into position in original text
int mirrorPosition( int const & pos ) const
{
int newPos = pos;
for( auto const accentPos: accentMarkPos)
{
if ( accentPos < pos )
newPos++;
else
break;
}
return newPos;
}
};
/// End of DslAccentMark class
/// DiacriticsHandler class
///
/// Remove diacritics from text
/// and mirror position in normalized text to original text
class DiacriticsHandler : public AccentMarkHandler
{
public:
DiacriticsHandler() = default;
~DiacriticsHandler() = default;
/// Create text without diacriticss
/// and store diacritic marks positions
void setText( QString const & baseString ) override
{
accentMarkPos.clear();
normalizedString.clear();
gd::wstring baseText = gd::toWString( baseString );
gd::wstring normText;
int pos = 0;
normText.reserve( baseText.size() );
gd::wchar const * nextChar = baseText.data();
size_t consumed;
for( size_t left = baseText.size(); left; )
{
if( *nextChar >= 0x10000U )
{
// Will be translated into surrogate pair
normText.push_back( *nextChar );
pos += 2;
nextChar++; left--;
continue;
}
gd::wchar ch = *nextChar;
if( Folding::isCombiningMark( ch ) )
{
accentMarkPos.append( pos );
nextChar++; left--;
continue;
}
normText.push_back( ch );
pos += 1;
nextChar += 1;
left -= 1;
}
normalizedString = QString::fromStdU32String( normText );
}
};
/// End of DiacriticsHandler class
void ArticleView::emitJavascriptFinished(){
emit notifyJavascriptFinished();
}
namespace {
@ -176,7 +61,7 @@ bool isScrollTo( QString const & id )
QString dictionaryIdFromScrollTo( QString const & scrollTo )
{
Q_ASSERT( isScrollTo( scrollTo ) );
const int scrollToPrefixLength = 7;
constexpr int scrollToPrefixLength = 7;
return scrollTo.mid( scrollToPrefixLength );
}
@ -361,8 +246,7 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, Au
// Variable name for store current selection range
rangeVarName = QString( "sr_%1" ).arg( QString::number( (quint64)this, 16 ) );
const bool fromMainWindow = parent && parent->objectName() == "MainWindow";
if ( fromMainWindow ) {
if ( const bool fromMainWindow = parent && parent->objectName() == "MainWindow" ) {
connect( GlobalBroadcaster::instance(),
&GlobalBroadcaster::dictionaryChanges,
this,
@ -437,9 +321,7 @@ void ArticleView::showDefinition( QString const & word, unsigned group,
delayedHighlightText.clear();
}
Contexts::Iterator pos = contexts.find( "gdanchor" );
if( pos != contexts.end() )
{
if ( Contexts::Iterator pos = contexts.find( "gdanchor" ); pos != contexts.end() ) {
Utils::Url::addQueryItem( req, "gdanchor", contexts[ "gdanchor" ] );
contexts.erase( pos );
}
@ -591,10 +473,7 @@ void ArticleView::handleUrlChanged( QUrl const & url )
{
QIcon icon;
unsigned group = getGroup( url );
if ( group )
{
if ( unsigned group = getGroup( url ) ) {
// Find the group's instance corresponding to the fragment value
for ( auto const & g : groups ) {
if ( g.id == group ) {
@ -632,17 +511,15 @@ void ArticleView::setActiveArticleId(QString const & dictId){
QString ArticleView::getCurrentArticle()
{
QString dictId=getActiveArticleId();
const QString dictId = getActiveArticleId();
return scrollToFromDictionaryId( dictId );
}
void ArticleView::jumpToDictionary( QString const & id, bool force )
{
QString targetArticle = scrollToFromDictionaryId( id );
// jump only if neceessary, or when forced
if ( force || targetArticle != getCurrentArticle() )
{
if ( const QString targetArticle = scrollToFromDictionaryId( id ); force || targetArticle != getCurrentArticle() ) {
setCurrentArticle( targetArticle, true );
}
}
@ -845,7 +722,7 @@ bool ArticleView::eventFilter( QObject * obj, QEvent * ev )
if( obj == webview ) {
if( ev->type() == QEvent::MouseButtonPress ) {
QMouseEvent * event = static_cast< QMouseEvent * >( ev );
auto event = static_cast< QMouseEvent * >( ev );
if ( event->button() == Qt::XButton1 ) {
back();
return true;
@ -858,7 +735,7 @@ bool ArticleView::eventFilter( QObject * obj, QEvent * ev )
else
if ( ev->type() == QEvent::KeyPress )
{
QKeyEvent * keyEvent = static_cast< QKeyEvent * >( ev );
auto keyEvent = static_cast< QKeyEvent * >( ev );
if ( keyEvent->modifiers() &
( Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier ) )
@ -920,17 +797,15 @@ QString ArticleView::getMutedForGroup( unsigned group )
if ( groupInstance )
{
for( unsigned x = 0; x < groupInstance->dictionaries.size(); ++x )
{
QString id = QString::fromStdString(
groupInstance->dictionaries[ x ]->getId() );
for ( const auto & dictionarie : groupInstance->dictionaries ) {
QString id = QString::fromStdString( dictionarie->getId() );
if ( mutedDictionaries->contains( id ) )
mutedDicts.append( id );
}
}
if ( mutedDicts.size() )
if ( !mutedDicts.empty() )
return mutedDicts.join( "," );
}
@ -955,8 +830,8 @@ QStringList ArticleView::getMutedDictionaries(unsigned group) {
QStringList mutedDicts;
if (groupInstance) {
for (unsigned x = 0; x < groupInstance->dictionaries.size(); ++x) {
QString id = QString::fromStdString(groupInstance->dictionaries[x]->getId());
for ( const auto & dictionarie : groupInstance->dictionaries ) {
QString id = QString::fromStdString( dictionarie->getId() );
if (mutedDictionaries->contains(id))
mutedDicts.append(id);
@ -1161,11 +1036,9 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const &
{
// Link to other dictionary
QString dictName( Utils::Url::queryItemValue( url, "dict" ) );
for( unsigned i = 0; i < allDictionaries.size(); i++ )
{
if( dictName.compare( QString::fromUtf8( allDictionaries[ i ]->getName().c_str() ) ) == 0 )
{
newScrollTo = scrollToFromDictionaryId( QString::fromUtf8( allDictionaries[ i ]->getId().c_str() ) );
for ( const auto & allDictionarie : allDictionaries ) {
if ( dictName.compare( QString::fromUtf8( allDictionarie->getName().c_str() ) ) == 0 ) {
newScrollTo = scrollToFromDictionaryId( QString::fromUtf8( allDictionarie->getId().c_str() ) );
break;
}
}
@ -1208,14 +1081,13 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const &
unsigned currentGroup = getGroup( ref );
std::vector< sptr< Dictionary::Class > > const * activeDicts = 0;
std::vector< sptr< Dictionary::Class > > const * activeDicts = nullptr;
if ( groups.size() )
{
for( unsigned x = 0; x < groups.size(); ++x )
if ( groups[ x ].id == currentGroup )
{
activeDicts = &( groups[ x ].dictionaries );
for ( const auto & group : groups )
if ( group.id == currentGroup ) {
activeDicts = &( group.dictionaries );
break;
}
}
@ -1351,11 +1223,8 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const &
// Program. Run it.
QString id( url.host() );
for( Config::Programs::const_iterator i = cfg.programs.begin();
i != cfg.programs.end(); ++i )
{
if ( i->id == id )
{
for ( const auto & program : cfg.programs ) {
if ( program.id == id ) {
// Found the corresponding program.
Programs::RunInstance * req = new Programs::RunInstance;
@ -1364,8 +1233,7 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const &
QString error;
// Delete the request if it fails to start
if ( !req->start( *i, url.path().mid( 1 ), error ) )
{
if ( !req->start( program, url.path().mid( 1 ), error ) ) {
delete req;
QMessageBox::critical( this, "GoldenDict",
@ -1426,14 +1294,13 @@ ResourceToSaveHandler * ArticleView::saveResource( const QUrl & url, const QUrl
unsigned currentGroup = getGroup( ref );
std::vector< sptr< Dictionary::Class > > const * activeDicts = 0;
std::vector< sptr< Dictionary::Class > > const * activeDicts = nullptr;
if ( groups.size() )
{
for( unsigned x = 0; x < groups.size(); ++x )
if ( groups[ x ].id == currentGroup )
{
activeDicts = &( groups[ x ].dictionaries );
for ( const auto & group : groups )
if ( group.id == currentGroup ) {
activeDicts = &( group.dictionaries );
break;
}
}
@ -1454,14 +1321,12 @@ ResourceToSaveHandler * ArticleView::saveResource( const QUrl & url, const QUrl
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() );
sptr< Dictionary::DataRequest > data_request =
( *activeDicts )[ x ]->getResource( url.path().mid( 1 ).toUtf8().data() );
handler->addRequest( req );
handler->addRequest( data_request );
if( req->isFinished() && req->dataSize() > 0 )
{
if ( data_request->isFinished() && data_request->dataSize() > 0 ) {
handler->downloadFinished();
return handler;
}
@ -1694,20 +1559,20 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
QWebEnginePage * r = webview->page();
QMenu menu( this );
QAction * followLink = 0;
QAction * followLinkExternal = 0;
QAction * followLinkNewTab = 0;
QAction * lookupSelection = 0;
QAction * lookupSelectionGr = 0;
QAction * lookupSelectionNewTab = 0;
QAction * lookupSelectionNewTabGr = 0;
QAction * maxDictionaryRefsAction = 0;
QAction * addWordToHistoryAction = 0;
QAction * addHeaderToHistoryAction = 0;
QAction * sendWordToInputLineAction = 0;
QAction * saveImageAction = 0;
QAction * saveSoundAction = 0;
QAction * saveBookmark = 0;
QAction * followLink = nullptr;
QAction * followLinkExternal = nullptr;
QAction * followLinkNewTab = nullptr;
QAction * lookupSelection = nullptr;
QAction * lookupSelectionGr = nullptr;
QAction * lookupSelectionNewTab = nullptr;
QAction * lookupSelectionNewTabGr = nullptr;
QAction * maxDictionaryRefsAction = nullptr;
QAction * addWordToHistoryAction = nullptr;
QAction * addHeaderToHistoryAction = nullptr;
QAction * sendWordToInputLineAction = nullptr;
QAction * saveImageAction = nullptr;
QAction * saveSoundAction = nullptr;
QAction * saveBookmark = nullptr;
#if( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
const QWebEngineContextMenuData * menuData = &(r->contextMenuData());
@ -1798,7 +1663,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
Instances::Group const * altGroup =
( groupComboBox && groupComboBox->getCurrentGroup() != getGroup( webview->url() ) ) ?
groups.findGroup( groupComboBox->getCurrentGroup() ) :
0;
nullptr;
if ( altGroup )
{
@ -1879,7 +1744,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
{
if ( allDictionaries[ x ]->getId() == i->toUtf8().data() )
{
QAction * action = 0;
QAction * action = nullptr;
if ( refsAdded == cfg.preferences.maxDictionaryRefsInContextMenu )
{
// Enough! Or the menu would become too large.
@ -2364,7 +2229,7 @@ bool ArticleView::closeSearch()
else
if( ftsSearchIsOpened )
{
allMatches.clear();
firstAvailableText.clear();
uniqueMatches.clear();
ftsPosition = 0;
ftsSearchIsOpened = false;
@ -2405,153 +2270,95 @@ void ArticleView::copyAsText()
void ArticleView::highlightFTSResults()
{
closeSearch();
// Clear any current selection
if( webview->selectedText().size() ) {
webview->page()->runJavaScript( "window.getSelection().removeAllRanges();_=0;" );
webview->findText( "" );
QString regString = Utils::Url::queryItemValue( webview->url(), "regexp" );
if ( regString.isEmpty() )
return;
//<div><i>watch</i>out</div> to plainText will return "watchout".
//if application goes here,that means the article text must contains the search text.
//whole word match regString will contain \b . can not match the above senario.
//workaround ,remove \b from the regstring="(\bwatch\b)"
regString.remove( QRegularExpression( "\\\\b" ) );
//webengine support diacritic text searching.
auto parts = regString.split( " ", Qt::SkipEmptyParts );
//get first part of string.
for ( auto const & p : parts ) {
if ( p.startsWith( "-" ) )
continue;
firstAvailableText = p;
break;
}
webview->page()->toPlainText(
[ & ]( const QString pageText ) {
const QUrl & url = webview->url();
if ( firstAvailableText.isEmpty() ) {
return;
}
QString regString = Utils::Url::queryItemValue( url, "regexp" );
if( regString.isEmpty() )
return;
bool ignoreDiacritics = Utils::Url::hasQueryItem( url, "ignore_diacritics" );
//remove possible wildcard character.
auto cleaned = firstAvailableText.split( QRegularExpression( "\\p{P}" ) );
if( ignoreDiacritics )
regString = QString::fromStdU32String( Folding::applyDiacriticsOnly( gd::toWString( regString ) ) );
else
regString = regString.remove( AccentMarkHandler::accentMark() );
if ( cleaned.empty() )
return;
//<div><i>watch</i>out</div> to plainText will return "watchout".
//if application goes here,that means the article text must contains the search text.
//whole word match regString will contain \b . can not match the above senario.
//workaround ,remove \b from the regstring="(\bwatch\b)"
regString.remove( QRegularExpression( "\\\\b" ) );
firstAvailableText = cleaned.at( 0 );
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
webview->findText( firstAvailableText,
QWebEnginePage::FindBackward,
[ & ]( const QWebEngineFindTextResult & result ) {
qInfo() << result.activeMatch() << "of" << result.numberOfMatches() << "matches";
QRegularExpression regexp;
if( Utils::Url::hasQueryItem( url, "wildcards" ) )
regexp.setPattern( wildcardsToRegexp( regString ) );
else
regexp.setPattern( regString );
if ( result.numberOfMatches() == 0 ) {
ftsSearchPanel->statusLabel->setText( searchStatusMessageNoMatches() );
}
else {
ftsSearchPanel->statusLabel->setText(
searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) );
}
QRegularExpression::PatternOptions patternOptions =
QRegularExpression::DotMatchesEverythingOption | QRegularExpression::UseUnicodePropertiesOption |
QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption;
if( !Utils::Url::hasQueryItem( url, "matchcase" ) )
patternOptions |= QRegularExpression::CaseInsensitiveOption;
regexp.setPatternOptions( patternOptions );
ftsSearchPanel->show();
ftsSearchPanel->previous->setEnabled( result.numberOfMatches() > 1 );
ftsSearchPanel->next->setEnabled( result.numberOfMatches() > 1 );
if( regexp.pattern().isEmpty() || !regexp.isValid() )
return;
sptr< AccentMarkHandler > marksHandler = ignoreDiacritics ? std::make_shared<DiacriticsHandler>() : std::make_shared<AccentMarkHandler>();
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 )
{
gdWarning( "ArticleView::highlightFTSResults(): Too long match - skipped (matched length %i, allowed %i)",
match.capturedLength(),
FTS::MaxMatchLengthForHighlightResults );
}
else
allMatches.append( pageText.mid( spos, matched ) );
}
ftsSearchMatchCase = Utils::Url::hasQueryItem( url, "matchcase" );
QWebEnginePage::FindFlags flags( QWebEnginePage::FindBackward );
if( ftsSearchMatchCase )
flags |= QWebEnginePage::FindCaseSensitively;
if( allMatches.isEmpty() )
ftsSearchPanel->statusLabel->setText( searchStatusMessageNoMatches() );
else {
// highlightAllFtsOccurences( flags );
webview->findText( allMatches.at( 0 ), flags );
// if( webview->findText( allMatches.at( 0 ), flags ) )
// {
// webview->page()->runJavaScript(
// QString( "%1=window.getSelection().getRangeAt(0);_=0;" ).arg( rangeVarName ) );
// }
Q_ASSERT( ftsPosition == 0 );
ftsSearchPanel->statusLabel->setText( searchStatusMessage( 1, allMatches.size() ) );
}
ftsSearchPanel->show();
ftsSearchPanel->previous->setEnabled( false );
ftsSearchPanel->next->setEnabled( allMatches.size() > 1 );
ftsSearchIsOpened = true;
} );
ftsSearchIsOpened = true;
} );
#else
webview->findText( firstAvailableText, QWebEnginePage::FindBackward, [ this ]( bool res ) {
ftsSearchPanel->previous->setEnabled( res );
if ( !ftsSearchPanel->next->isEnabled() )
ftsSearchPanel->next->setEnabled( res );
} );
#endif
}
void ArticleView::highlightAllFtsOccurences( QWebEnginePage::FindFlags flags )
void ArticleView::setActiveDictIds( const ActiveDictIds & ad )
{
// Usually allMatches contains mostly duplicates. Thus searching for each element of
// allMatches to highlight them takes a long time => collect unique elements into a
// set and search for them instead.
// Don't use QList::toSet() or QSet's range constructor because they reserve space
// for QList::size() elements, whereas the final QSet size is likely 1 or 2.
QSet< QString > uniqueMatches;
for( int x = 0; x < allMatches.size(); ++x )
{
QString const & match = allMatches.at( x );
// Consider words that differ only in case equal if the search is case-insensitive.
uniqueMatches.insert( ftsSearchMatchCase ? match : match.toLower() );
if ( ( ad.word == currentWord && ad.groupId == getCurrentGroup() ) || historyMode ) {
// ignore all other signals.
qDebug() << "receive dicts, current word:" << currentWord << ad.word << ":" << ad.dictIds;
currentActiveDictIds << ad.dictIds;
currentActiveDictIds.removeDuplicates();
emit updateFoundInDictsList();
}
for( QSet< QString >::const_iterator it = uniqueMatches.constBegin(); it != uniqueMatches.constEnd(); ++it )
webview->findText( *it, flags );
}
void ArticleView::setActiveDictIds(ActiveDictIds ad) {
if ( ( ad.word == currentWord && ad.groupId == getCurrentGroup() ) || historyMode)
{
// ignore all other signals.
qDebug() << "receive dicts, current word:" << currentWord << ad.word << ":" << ad.dictIds;
currentActiveDictIds << ad.dictIds;
currentActiveDictIds.removeDuplicates();
emit updateFoundInDictsList();
}
}
void ArticleView::dictionaryClear( ActiveDictIds ad )
void ArticleView::dictionaryClear( const ActiveDictIds & ad )
{
// ignore all other signals.
if( ad.word == currentWord && ad.groupId==getCurrentGroup() )
{
if ( ad.word == currentWord && ad.groupId == getCurrentGroup() ) {
qDebug() << "clear current dictionaries:" << currentWord;
currentActiveDictIds.clear();
}
}
//todo ,futher refinement?
void ArticleView::performFtsFindOperation( bool backwards )
{
if( !ftsSearchIsOpened )
return;
if( allMatches.isEmpty() ) {
if ( firstAvailableText.isEmpty() ) {
ftsSearchPanel->statusLabel->setText( searchStatusMessageNoMatches() );
ftsSearchPanel->next->setEnabled( false );
ftsSearchPanel->previous->setEnabled( false );
@ -2568,12 +2375,9 @@ void ArticleView::performFtsFindOperation( bool backwards )
webview->page()->runJavaScript(
QString( "var sel=window.getSelection();sel.removeAllRanges();sel.addRange(%1);_=0;" ).arg( rangeVarName ) );
if( backwards ) {
if( ftsPosition > 0 ) {
ftsPosition -= 1;
}
if ( backwards ) {
#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
webview->findText( allMatches.at( ftsPosition ),
webview->findText( firstAvailableText,
flags | QWebEnginePage::FindBackward,
[ this ]( const QWebEngineFindTextResult & result ) {
if( result.numberOfMatches() == 0 )
@ -2581,9 +2385,12 @@ void ArticleView::performFtsFindOperation( bool backwards )
ftsSearchPanel->previous->setEnabled( true );
if( !ftsSearchPanel->next->isEnabled() )
ftsSearchPanel->next->setEnabled( true );
ftsSearchPanel->statusLabel->setText(
searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) );
} );
#else
webview->findText( allMatches.at( ftsPosition ), flags | QWebEnginePage::FindBackward, [ this ]( bool res ) {
webview->findText( firstAvailableText, flags | QWebEnginePage::FindBackward, [ this ]( bool res ) {
ftsSearchPanel->previous->setEnabled( res );
if( !ftsSearchPanel->next->isEnabled() )
ftsSearchPanel->next->setEnabled( res );
@ -2591,30 +2398,26 @@ void ArticleView::performFtsFindOperation( bool backwards )
#endif
}
else {
if( ftsPosition < allMatches.size() - 1 ) {
ftsPosition += 1;
}
#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
webview->findText( allMatches.at( ftsPosition ), flags, [ this ]( const QWebEngineFindTextResult & result ) {
webview->findText( firstAvailableText, flags, [ this ]( const QWebEngineFindTextResult & result ) {
if( result.numberOfMatches() == 0 )
return;
ftsSearchPanel->next->setEnabled( true );
if( !ftsSearchPanel->previous->isEnabled() )
ftsSearchPanel->previous->setEnabled( true );
ftsSearchPanel->statusLabel->setText( searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) );
} );
}
#else
webview->findText( allMatches.at( ftsPosition ), flags, [ this ]( bool res ) {
webview->findText( firstAvailableText, flags, [ this ]( bool res ) {
ftsSearchPanel->next->setEnabled( res );
if( !ftsSearchPanel->previous->isEnabled() )
if ( !ftsSearchPanel->previous->isEnabled() )
ftsSearchPanel->previous->setEnabled( res );
} );
}
#endif
ftsSearchPanel->statusLabel->setText( searchStatusMessage( ftsPosition + 1, allMatches.size() ) );
}
}
void ArticleView::on_ftsSearchPrevious_clicked()
@ -2627,18 +2430,17 @@ void ArticleView::on_ftsSearchNext_clicked()
performFtsFindOperation( false );
}
ResourceToSaveHandler::ResourceToSaveHandler(ArticleView * view, QString const & fileName ) :
ResourceToSaveHandler::ResourceToSaveHandler( ArticleView * view, QString fileName ):
QObject( view ),
fileName( fileName ),
fileName( std::move( fileName ) ),
alreadyDone( false )
{
connect( this, &ResourceToSaveHandler::statusBarMessage, view, &ArticleView::statusBarMessage );
}
void ResourceToSaveHandler::addRequest( sptr< Dictionary::DataRequest > req )
void ResourceToSaveHandler::addRequest( const sptr< Dictionary::DataRequest > & req )
{
if( !alreadyDone )
{
if ( !alreadyDone ) {
downloadRequests.push_back( req );
connect( req.get(), &Dictionary::Request::finished, this, &ResourceToSaveHandler::downloadFinished );
@ -2651,22 +2453,18 @@ void ResourceToSaveHandler::downloadFinished()
return; // Stray signal
// Find any finished resources
for( list< sptr< Dictionary::DataRequest > >::iterator i =
downloadRequests.begin(); i != downloadRequests.end(); )
{
if ( (*i)->isFinished() )
{
if ( (*i)->dataSize() >= 0 && !alreadyDone )
{
for ( auto i = downloadRequests.begin(); i != downloadRequests.end(); ) {
if ( ( *i )->isFinished() ) {
if ( ( *i )->dataSize() >= 0 && !alreadyDone ) {
QByteArray resourceData;
vector< char > const & data = (*i)->getFullData();
resourceData = QByteArray( data.data(), data.size() );
vector< char > const & data = ( *i )->getFullData();
resourceData = QByteArray( data.data(), data.size() );
// Write data to file
if ( !fileName.isEmpty() )
{
QFileInfo fileInfo( fileName );
const QFileInfo fileInfo( fileName );
QDir().mkpath( fileInfo.absoluteDir().absolutePath() );
QFile file( fileName );

View file

@ -84,7 +84,7 @@ class ArticleView: public QWidget
QString activeDictId;
/// Search in results of full-text search
QStringList allMatches;
QString firstAvailableText;
QStringList uniqueMatches;
bool ftsSearchIsOpened = false;
bool ftsSearchMatchCase = false;
@ -93,7 +93,6 @@ class ArticleView: public QWidget
QString delayedHighlightText;
void highlightFTSResults();
void highlightAllFtsOccurences( QWebEnginePage::FindFlags flags );
void performFtsFindOperation( bool backwards );
public:
@ -127,8 +126,6 @@ public:
/// Returns "gdfrom-" + dictionaryId.
static QString scrollToFromDictionaryId( QString const & dictionaryId );
void emitJavascriptFinished();
/// Shows the definition of the given word with the given group.
/// scrollTo can be optionally set to a "gdfrom-xxxx" identifier to position
/// the page to that article on load.
@ -380,9 +377,9 @@ private slots:
/// Copy current selection as plain text
void copyAsText();
void setActiveDictIds(ActiveDictIds);
void setActiveDictIds( const ActiveDictIds & ad );
void dictionaryClear( ActiveDictIds ad );
void dictionaryClear( const ActiveDictIds & ad );
private:
@ -417,7 +414,7 @@ private:
/// Attempts removing last temporary file created.
void cleanupTemp();
bool eventFilter( QObject * obj, QEvent * ev );
bool eventFilter( QObject * obj, QEvent * ev ) override;
void performFindOperation( bool restart, bool backwards, bool checkHighlight = false );
@ -428,9 +425,8 @@ private:
QStringList getMutedDictionaries(unsigned group);
protected:
// We need this to hide the search bar when we're showed
void showEvent( QShowEvent * );
// We need this to hide the search bar when we're showed
void showEvent( QShowEvent * ) override;
};
class ResourceToSaveHandler: public QObject
@ -438,10 +434,12 @@ class ResourceToSaveHandler: public QObject
Q_OBJECT
public:
explicit ResourceToSaveHandler( ArticleView * view, QString const & fileName );
void addRequest( sptr< Dictionary::DataRequest > req );
explicit ResourceToSaveHandler( ArticleView * view, QString fileName );
void addRequest( const sptr< Dictionary::DataRequest > & req );
bool isEmpty()
{ return downloadRequests.empty(); }
{
return downloadRequests.empty();
}
signals:
void done();
@ -462,7 +460,7 @@ class ArticleViewAgent : public QObject
ArticleView * articleView;
public:
ArticleViewAgent( ArticleView * articleView );
explicit ArticleViewAgent( ArticleView * articleView );
public slots: