mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-23 20:14:05 +00:00
257 lines
6.1 KiB
C++
257 lines
6.1 KiB
C++
#ifndef __FULLTEXTSEARCH_HH_INCLUDED__
|
|
#define __FULLTEXTSEARCH_HH_INCLUDED__
|
|
|
|
#include <QSemaphore>
|
|
#include <QStringList>
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
|
|
#include <QtCore5Compat/QRegExp>
|
|
#else
|
|
#include <QRegExp>
|
|
#endif
|
|
#include <QAbstractListModel>
|
|
#include <QList>
|
|
#include <QAction>
|
|
|
|
#include "dictionary.hh"
|
|
#include "ui_fulltextsearch.h"
|
|
#include "mutex.hh"
|
|
#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
|
|
// (QWebEnginePage::findText() crashes on too long strings)
|
|
MaxMatchLengthForHighlightResults = 500
|
|
};
|
|
|
|
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 )
|
|
{
|
|
dictIDs.append( dictid_ );
|
|
}
|
|
|
|
QString trimQuotes( QString const & ) const;
|
|
|
|
bool operator <( FtsHeadword const & other ) const;
|
|
|
|
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;
|
|
QTimer * timer;
|
|
QThread * timerThread;
|
|
|
|
public:
|
|
Indexing( QAtomicInt & cancelled, std::vector< sptr< Dictionary::Class > > const & dicts,
|
|
QSemaphore & hasExited_):
|
|
isCancelled( cancelled ),
|
|
dictionaries( dicts ),
|
|
hasExited( hasExited_ ),
|
|
timer(new QTimer(nullptr)), // must be null since it will live in separate thread
|
|
timerThread(new QThread(this))
|
|
{
|
|
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);
|
|
|
|
}
|
|
|
|
~Indexing()
|
|
{
|
|
|
|
|
|
emit sendNowIndexingName( QString() );
|
|
hasExited.release();
|
|
}
|
|
|
|
virtual void run();
|
|
|
|
signals:
|
|
void sendNowIndexingName( QString );
|
|
|
|
private slots:
|
|
void timeout();
|
|
};
|
|
|
|
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(); }
|
|
|
|
/// Start dictionaries indexing for full-text search
|
|
void doIndexing();
|
|
|
|
/// Break indexing thread
|
|
void stopIndexing();
|
|
|
|
QString nowIndexingName();
|
|
|
|
protected:
|
|
QAtomicInt isCancelled;
|
|
QSemaphore indexingExited;
|
|
std::vector< sptr< Dictionary::Class > > dictionaries;
|
|
bool started;
|
|
QString nowIndexing;
|
|
Mutex nameMutex;
|
|
|
|
private slots:
|
|
void setNowIndexedName( QString name );
|
|
|
|
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;
|
|
bool ignoreWordsOrder;
|
|
bool ignoreDiacritics;
|
|
|
|
std::list< sptr< Dictionary::DataRequest > > searchReqs;
|
|
|
|
FtsIndexing & ftsIdx;
|
|
|
|
QRegExp searchRegExp;
|
|
int matchedCount;
|
|
|
|
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 setCurrentGroup( unsigned group_ )
|
|
{ group = group_; updateDictionaries(); }
|
|
|
|
void stopSearch();
|
|
|
|
protected:
|
|
bool eventFilter( QObject * obj, QEvent * ev );
|
|
|
|
private:
|
|
Ui::FullTextSearchDialog ui;
|
|
QList< FtsHeadword > results;
|
|
HeadwordsListModel * model;
|
|
WordListItemDelegate * delegate;
|
|
QAction helpAction;
|
|
|
|
void showDictNumbers();
|
|
|
|
private slots:
|
|
void setNewIndexingName( QString );
|
|
void saveData();
|
|
void accept();
|
|
void setLimitsUsing();
|
|
void ignoreWordsOrderClicked();
|
|
void ignoreDiacriticsClicked();
|
|
void searchReqFinished();
|
|
void matchCount(int);
|
|
void reject();
|
|
void itemClicked( QModelIndex const & idx );
|
|
void updateDictionaries();
|
|
void helpRequested();
|
|
|
|
signals:
|
|
void showTranslationFor( QString const &, QStringList const & dictIDs,
|
|
QRegExp const & searchRegExp, bool ignoreDiacritics );
|
|
void closeDialog();
|
|
};
|
|
|
|
|
|
} // namespace FTS
|
|
|
|
#endif // __FULLTEXTSEARCH_HH_INCLUDED__
|