goldendict-ng/src/fulltextsearch.hh

258 lines
6.1 KiB
C++
Raw Normal View History

2014-04-16 16:18:28 +00:00
#ifndef __FULLTEXTSEARCH_HH_INCLUDED__
#define __FULLTEXTSEARCH_HH_INCLUDED__
#include <QAbstractListModel>
#include <QAction>
#include <QList>
#include <QTimer>
#include <QThread>
#include <QRunnable>
2014-04-16 16:18:28 +00:00
#include <QSemaphore>
#include <QStringList>
2022-02-27 05:17:37 +00:00
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
2022-02-27 14:42:40 +00:00
#include <QtCore5Compat/QRegExp>
#else
#include <QRegExp>
2022-02-27 05:17:37 +00:00
#endif
2014-04-16 16:18:28 +00:00
#include "dict/dictionary.hh"
2014-04-16 16:18:28 +00:00
#include "ui_fulltextsearch.h"
2014-04-16 16:18:28 +00:00
#include "config.hh"
#include "instances.hh"
#include "delegate.hh"
namespace FTS
{
enum {
// Minimum word length for indexing
MinimumWordSize = 4,
// Maximum dictionary size for first iteration of FTS indexing
MaxDictionarySizeForFastSearch = 150000,
// Maxumum match length for highlight search results
2021-07-06 13:01:50 +00:00
// (QWebEnginePage::findText() crashes on too long strings)
MaxMatchLengthForHighlightResults = 500
2014-04-16 16:18:28 +00:00
};
enum SearchMode
{
WholeWords = 0,
PlainText,
Wildcards,
RegExp
};
struct FtsHeadword
{
QString headword;
QStringList dictIDs;
QStringList foundHiliteRegExps;
bool matchCase;
FtsHeadword( QString const & headword_, QString const & dictid_,
QStringList hilites, bool match_case ) :
headword( headword_ ),
foundHiliteRegExps( hilites ),
matchCase( match_case )
2014-04-16 16:18:28 +00:00
{
dictIDs.append( dictid_ );
}
QString trimQuotes( QString const & ) const;
bool operator <( FtsHeadword const & other ) const;
2014-04-16 16:18:28 +00:00
bool operator ==( FtsHeadword const & other ) const
{ return headword.compare( other.headword, Qt::CaseInsensitive ) == 0; }
bool operator !=( FtsHeadword const & other ) const
{ return headword.compare( other.headword, Qt::CaseInsensitive ) != 0; }
};
class Indexing : public QObject, public QRunnable
{
Q_OBJECT
QAtomicInt & isCancelled;
std::vector< sptr< Dictionary::Class > > const & dictionaries;
QSemaphore & hasExited;
2022-10-06 11:32:45 +00:00
QTimer * timer;
QThread * timerThread;
2014-04-16 16:18:28 +00:00
public:
Indexing( QAtomicInt & cancelled, std::vector< sptr< Dictionary::Class > > const & dicts, QSemaphore & hasExited_ ):
2014-04-16 16:18:28 +00:00
isCancelled( cancelled ),
dictionaries( dicts ),
2022-10-06 11:32:45 +00:00
hasExited( hasExited_ ),
timer( new QTimer( nullptr ) ), // must be null since it will live in separate thread
timerThread( new QThread( this ) )
2022-10-06 11:32:45 +00:00
{
connect( timer, &QTimer::timeout, this, &Indexing::timeout );
timer->moveToThread( timerThread );
connect( timerThread, &QThread::started, timer, [ this ]() {
timer->start( 2000 );
} );
connect( timerThread, &QThread::finished, timer, &QTimer::stop );
connect( timerThread, &QThread::finished, timer, &QObject::deleteLater );
2022-10-06 11:32:45 +00:00
}
2014-04-16 16:18:28 +00:00
~Indexing()
{
2022-10-06 11:32:45 +00:00
emit sendNowIndexingName( QString() );
2014-04-16 16:18:28 +00:00
hasExited.release();
}
virtual void run();
signals:
void sendNowIndexingName( QString );
2022-10-06 11:32:45 +00:00
private slots:
void timeout();
2014-04-16 16:18:28 +00:00
};
class FtsIndexing : public QObject
{
Q_OBJECT
public:
FtsIndexing( std::vector< sptr< Dictionary::Class > > const & dicts );
virtual ~FtsIndexing()
{ stopIndexing(); }
void setDictionaries( std::vector< sptr< Dictionary::Class > > const & dicts )
{
clearDictionaries();
dictionaries = dicts;
}
void clearDictionaries()
{
dictionaries.clear();
}
2014-04-16 16:18:28 +00:00
/// Start dictionaries indexing for full-text search
void doIndexing();
/// Break indexing thread
void stopIndexing();
QString nowIndexingName();
private:
2014-04-16 16:18:28 +00:00
QAtomicInt isCancelled;
QSemaphore indexingExited;
std::vector< sptr< Dictionary::Class > > dictionaries;
2014-04-16 16:18:28 +00:00
bool started;
QString nowIndexing;
QMutex nameMutex;
2014-04-16 16:18:28 +00:00
private slots:
void setNowIndexedName( const QString & name );
2014-04-16 16:18:28 +00:00
signals:
void newIndexingName( QString name );
};
/// A model to be projected into the view, according to Qt's MVC model
class HeadwordsListModel: public QAbstractListModel
{
Q_OBJECT
public:
HeadwordsListModel( QWidget * parent, QList< FtsHeadword > & headwords_,
std::vector< sptr< Dictionary::Class > > const & dicts ):
QAbstractListModel( parent ), headwords( headwords_ ),
dictionaries( dicts )
{}
int rowCount( QModelIndex const & parent ) const;
QVariant data( QModelIndex const & index, int role ) const;
// bool insertRows( int row, int count, const QModelIndex & parent );
// bool removeRows( int row, int count, const QModelIndex & parent );
// bool setData( QModelIndex const & index, const QVariant & value, int role );
void addResults(const QModelIndex & parent, QList< FtsHeadword > const & headwords );
bool clear();
private:
QList< FtsHeadword > & headwords;
std::vector< sptr< Dictionary::Class > > const & dictionaries;
int getDictIndex( QString const & id ) const;
signals:
void contentChanged();
};
class FullTextSearchDialog : public QDialog
{
Q_OBJECT
Config::Class & cfg;
std::vector< sptr< Dictionary::Class > > const & dictionaries;
std::vector< Instances::Group > const & groups;
unsigned group;
std::vector< sptr< Dictionary::Class > > activeDicts;
std::list< sptr< Dictionary::DataRequest > > searchReqs;
FtsIndexing & ftsIdx;
QRegExp searchRegExp;
int matchedCount;
2014-04-23 14:19:46 +00:00
2014-04-16 16:18:28 +00:00
public:
FullTextSearchDialog( QWidget * parent,
Config::Class & cfg_,
std::vector< sptr< Dictionary::Class > > const & dictionaries_,
std::vector< Instances::Group > const & groups_,
FtsIndexing & ftsidx );
virtual ~FullTextSearchDialog();
void setSearchText( const QString & text );
2014-04-16 16:18:28 +00:00
void setCurrentGroup( unsigned group_ )
{ group = group_; updateDictionaries(); }
void stopSearch();
protected:
bool eventFilter( QObject * obj, QEvent * ev );
2014-04-16 16:18:28 +00:00
private:
Ui::FullTextSearchDialog ui;
QList< FtsHeadword > results;
HeadwordsListModel * model;
WordListItemDelegate * delegate;
2014-06-24 13:55:06 +00:00
QAction helpAction;
2014-04-16 16:18:28 +00:00
void showDictNumbers();
private slots:
void setNewIndexingName( QString );
void saveData();
void accept();
void searchReqFinished();
void matchCount(int);
2014-04-16 16:18:28 +00:00
void reject();
void itemClicked( QModelIndex const & idx );
void updateDictionaries();
signals:
void showTranslationFor( QString const &, QStringList const & dictIDs,
QRegExp const & searchRegExp, bool ignoreDiacritics );
2014-04-16 16:18:28 +00:00
void closeDialog();
};
} // namespace FTS
#endif // __FULLTEXTSEARCH_HH_INCLUDED__