Merge branch 'staged' into dev

This commit is contained in:
Xiao Yi Fang 2024-06-28 08:57:49 +08:00
commit 9ef50e807d
17 changed files with 216 additions and 407 deletions

View file

@ -1157,13 +1157,9 @@ void BtreeIndex::findArticleLinks( QVector< WordArticleLink > * articleLinks,
}
}
void BtreeIndex::findHeadWords( QSet< uint32_t > offsets, int & index, QSet< QString > * headwords, uint32_t length )
void BtreeIndex::findHeadWords( QList< uint32_t > offsets, int & index, QSet< QString > * headwords, uint32_t length )
{
int i = 0;
for ( auto begin = offsets.begin(); begin != offsets.end(); begin++, i++ ) {
if ( i < index ) {
continue;
}
for ( auto begin = offsets.begin() + index; begin != offsets.end(); begin++ ) {
findSingleNodeHeadwords( *begin, headwords );
index++;
@ -1207,8 +1203,8 @@ void BtreeIndex::findSingleNodeHeadwords( uint32_t offsets, QSet< QString > * he
}
}
//find the next chain ptr ,which is large than this currentChainPtr
QSet< uint32_t > BtreeIndex::findNodes()
//find the next chain ptr ,which is larger than this currentChainPtr
QList< uint32_t > BtreeIndex::findNodes()
{
QMutexLocker _( idxFileMutex );
@ -1219,12 +1215,12 @@ QSet< uint32_t > BtreeIndex::findNodes()
}
char const * leaf = &rootNode.front();
QSet< uint32_t > leafOffset;
QList< uint32_t > leafOffset;
uint32_t leafEntries;
leafEntries = *(uint32_t *)leaf;
if ( leafEntries != 0xffffFFFF ) {
leafOffset.insert( rootOffset );
leafOffset.append( rootOffset );
return leafOffset;
}
@ -1234,8 +1230,9 @@ QSet< uint32_t > BtreeIndex::findNodes()
uint32_t * offsets = (uint32_t *)leaf + 1;
uint32_t i = 0;
while ( i++ < ( indexNodeSize + 1 ) )
leafOffset.insert( *( offsets++ ) );
while ( i++ < ( indexNodeSize + 1 ) ) {
leafOffset.append( *( offsets++ ) );
}
return leafOffset;
}

View file

@ -100,9 +100,9 @@ public:
QSet< QString > * headwords,
QAtomicInt * isCancelled = 0 );
void findHeadWords( QSet< uint32_t > offsets, int & index, QSet< QString > * headwords, uint32_t length );
void findHeadWords( QList< uint32_t > offsets, int & index, QSet< QString > * headwords, uint32_t length );
void findSingleNodeHeadwords( uint32_t offsets, QSet< QString > * headwords );
QSet< uint32_t > findNodes();
QList< uint32_t > findNodes();
/// Retrieve headwords for presented article addresses
void
@ -197,15 +197,6 @@ public:
return 0;
}
// Sort articles offsets for full-text search in dictionary-specific order
// to increase of articles retrieving speed
// Default - simple sorting in increase order
virtual void sortArticlesOffsetsForFTS( QVector< uint32_t > & offsets, QAtomicInt & isCancelled )
{
Q_UNUSED( isCancelled );
std::sort( offsets.begin(), offsets.end() );
}
/// Called before each matching operation to ensure that any child init
/// has completed. Mainly used for deferred init. The default implementation
/// does nothing.

View file

@ -636,7 +636,6 @@ public:
quint64 getArticlePos( uint32_t articleNumber );
void sortArticlesOffsetsForFTS( QVector< uint32_t > & offsets, QAtomicInt & isCancelled ) override;
void makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration ) override;
void setFTSParameters( Config::FullTextSearch const & fts ) override
@ -888,11 +887,6 @@ quint64 SlobDictionary::getArticlePos( uint32_t articleNumber )
return ( ( (quint64)( entry.binIndex ) ) << 32 ) | entry.itemIndex;
}
void SlobDictionary::sortArticlesOffsetsForFTS( QVector< uint32_t > & offsets, QAtomicInt & isCancelled )
{
//Currently , we use xapian to create the fulltext index. The order of offsets is no important.
}
void SlobDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
{
if ( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )

View file

@ -225,8 +225,6 @@ public:
&& ( fts.maxDictionarySize == 0 || getArticleCount() <= fts.maxDictionarySize );
}
void sortArticlesOffsetsForFTS( QVector< uint32_t > & offsets, QAtomicInt & isCancelled ) override;
protected:
void loadIcon() noexcept override;
@ -504,25 +502,6 @@ void ZimDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration
}
}
void ZimDictionary::sortArticlesOffsetsForFTS( QVector< uint32_t > & offsets, QAtomicInt & isCancelled )
{
QVector< QPair< quint32, uint32_t > > offsetsWithClusters;
offsetsWithClusters.reserve( offsets.size() );
for ( QVector< uint32_t >::ConstIterator it = offsets.constBegin(); it != offsets.constEnd(); ++it ) {
if ( Utils::AtomicInt::loadAcquire( isCancelled ) )
return;
QMutexLocker _( &zimMutex );
offsetsWithClusters.append( QPair< uint32_t, quint32 >( getArticleCluster( df, *it ), *it ) );
}
std::sort( offsetsWithClusters.begin(), offsetsWithClusters.end() );
for ( int i = 0; i < offsetsWithClusters.size(); i++ )
offsets[ i ] = offsetsWithClusters.at( i ).second;
}
void ZimDictionary::getArticleText( uint32_t articleAddress, QString & headword, QString & text )
{
try {

View file

@ -91,8 +91,6 @@ void makeFTSIndex( BtreeIndexing::BtreeDictionary * dict, QAtomicInt & isCancell
if ( Utils::AtomicInt::loadAcquire( isCancelled ) )
throw exUserAbort();
// dict->sortArticlesOffsetsForFTS( offsets, isCancelled );
// incremental build the index.
// get the last address.
bool skip = true;

View file

@ -453,26 +453,8 @@ void FullTextSearchDialog::itemClicked( const QModelIndex & idx )
auto searchText = ui.searchLine->text();
searchText.replace( RX::Ftx::tokenBoundary, " " );
auto it = RX::Ftx::token.globalMatch( searchText );
QString firstAvailbeItem;
while ( it.hasNext() ) {
QRegularExpressionMatch match = it.next();
auto p = match.captured();
if ( p.startsWith( '-' ) )
continue;
//the searched text should be like "term".remove enclosed double quotation marks.
if ( p.startsWith( "\"" ) ) {
p.remove( "\"" );
}
firstAvailbeItem = p;
break;
}
if ( !firstAvailbeItem.isEmpty() ) {
reg = QRegularExpression( firstAvailbeItem, QRegularExpression::CaseInsensitiveOption );
if ( !searchText.isEmpty() ) {
reg = QRegularExpression( searchText, QRegularExpression::CaseInsensitiveOption );
}
emit showTranslationFor( headword, results[ idx.row() ].dictIDs, reg, false );

View file

@ -5,6 +5,7 @@ HeadwordListModel::HeadwordListModel( QObject * parent ):
QAbstractListModel( parent ),
filtering( false ),
totalSize( 0 ),
finished( false ),
index( 0 ),
ptr( nullptr )
{
@ -22,7 +23,7 @@ int HeadwordListModel::totalCount() const
bool HeadwordListModel::isFinish() const
{
return words.size() >= totalSize;
return finished || ( words.size() >= totalSize );
}
// export headword
@ -35,15 +36,38 @@ QString HeadwordListModel::getRow( int row )
return fileSortedList.at( row );
}
void HeadwordListModel::setFilter( QRegularExpression reg )
void HeadwordListModel::setFilter( const QRegularExpression & reg )
{
if ( reg.pattern().isEmpty() ) {
filtering = false;
//if the headword is already finished loaded, do nothing。
if ( finished ) {
return;
}
filtering = true;
//back to normal state ,restore the original model;
if ( reg.pattern().isEmpty() ) {
QMutexLocker _( &lock );
//race condition.
if ( !filtering ) {
return;
}
filtering = false;
//reset to previous models
beginResetModel();
words = QStringList( original_words );
endResetModel();
return;
}
else {
QMutexLocker _( &lock );
//the first time to enter filtering mode.
if ( !filtering ) {
filtering = true;
original_words = QStringList( words );
}
}
filterWords.clear();
auto sr = _dict->prefixMatch( gd::removeTrailingZero( reg.pattern() ), 500 );
auto sr = _dict->prefixMatch( gd::removeTrailingZero( reg.pattern() ), maxFilterResults );
connect( sr.get(), &Dictionary::Request::finished, this, &HeadwordListModel::requestFinished, Qt::QueuedConnection );
queuedRequests.push_back( sr );
}
@ -54,25 +78,6 @@ void HeadwordListModel::appendWord( const QString & word )
words.append( word );
}
void HeadwordListModel::addMatches( QStringList matches )
{
QStringList filtered;
for ( auto const & w : matches ) {
if ( !containWord( w ) ) {
filtered << w;
}
}
if ( filtered.isEmpty() )
return;
beginInsertRows( QModelIndex(), words.size(), words.size() + filtered.count() - 1 );
for ( const auto & word : filtered ) {
appendWord( word );
}
endInsertRows();
}
void HeadwordListModel::requestFinished()
{
// See how many new requests have finished, and if we have any new results
@ -81,8 +86,7 @@ void HeadwordListModel::requestFinished()
if ( !( *i )->getErrorString().isEmpty() ) {
qDebug() << "error:" << ( *i )->getErrorString();
}
if ( ( *i )->matchesCount() ) {
else if ( ( *i )->matchesCount() ) {
auto allmatches = ( *i )->getAllMatches();
for ( auto & match : allmatches )
filterWords.append( QString::fromStdU32String( match.word ) );
@ -94,19 +98,13 @@ void HeadwordListModel::requestFinished()
}
if ( queuedRequests.empty() ) {
QStringList filtered;
for ( auto & w : filterWords ) {
if ( !containWord( w ) ) {
filtered << w;
}
}
if ( filtered.isEmpty() )
if ( filterWords.isEmpty() ) {
return;
beginInsertRows( QModelIndex(), words.size(), words.size() + filtered.count() - 1 );
for ( const auto & word : filtered )
appendWord( word );
endInsertRows();
}
beginResetModel();
words = QStringList( filterWords );
endResetModel();
emit numberPopulated( words.size() );
}
}
@ -131,16 +129,29 @@ QVariant HeadwordListModel::data( const QModelIndex & index, int role ) const
bool HeadwordListModel::canFetchMore( const QModelIndex & parent ) const
{
if ( parent.isValid() || filtering )
if ( parent.isValid() || filtering || finished )
return false;
return ( words.size() < totalSize );
}
void HeadwordListModel::fetchMore( const QModelIndex & parent )
{
if ( parent.isValid() || filtering )
if ( parent.isValid() || filtering || finished )
return;
//arbitrary number
if ( totalSize < HEADWORDS_MAX_LIMIT ) {
finished = true;
beginInsertRows( QModelIndex(), 0, totalSize - 1 );
_dict->getHeadwords( words );
endInsertRows();
emit numberPopulated( words.size() );
return;
}
QSet< QString > headword;
QMutexLocker _( &lock );
@ -149,15 +160,9 @@ void HeadwordListModel::fetchMore( const QModelIndex & parent )
return;
}
QSet< QString > filtered;
beginInsertRows( QModelIndex(), words.size(), words.size() + headword.count() - 1 );
for ( const auto & word : std::as_const( headword ) ) {
if ( !containWord( word ) )
filtered.insert( word );
}
beginInsertRows( QModelIndex(), words.size(), words.size() + filtered.count() - 1 );
for ( const auto & word : filtered ) {
appendWord( word );
words << word;
}
endInsertRows();
@ -174,6 +179,11 @@ bool HeadwordListModel::containWord( const QString & word )
return hashedWords.contains( word );
}
void HeadwordListModel::setMaxFilterResults( int _maxFilterResults )
{
this->maxFilterResults = _maxFilterResults;
}
QSet< QString > HeadwordListModel::getRemainRows( int & nodeIndex )
{
QSet< QString > headword;

View file

@ -6,6 +6,7 @@
#include <QAbstractListModel>
#include <QStringList>
static const int HEADWORDS_MAX_LIMIT = 500000;
class HeadwordListModel: public QAbstractListModel
{
Q_OBJECT
@ -19,15 +20,14 @@ public:
bool isFinish() const;
QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const override;
QString getRow( int row );
void setFilter( QRegularExpression );
void setFilter( const QRegularExpression & );
void appendWord( const QString & word );
void addMatches( QStringList matches );
int getCurrentIndex() const;
bool containWord( const QString & word );
QSet< QString > getRemainRows( int & nodeIndex );
void setMaxFilterResults( int _maxFilterResults );
signals:
void numberPopulated( int number );
void finished( int number );
public slots:
void setDict( Dictionary::Class * dict );
@ -39,11 +39,14 @@ protected:
private:
QStringList words;
QStringList original_words;
QSet< QString > hashedWords;
QStringList filterWords;
bool filtering;
QStringList fileSortedList;
long totalSize;
int maxFilterResults;
bool finished;
Dictionary::Class * _dict;
int index;
char * ptr;

View file

@ -2,57 +2,14 @@
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "keyboardstate.hh"
#include <QObject> // To get Qt Q_OS defines
#ifdef Q_OS_WIN32
#include <windows.h>
#elif defined( HAVE_X11 )
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
#include <QGuiApplication>
#else
#include <QX11Info>
#endif
#include <X11/X.h>
#include <X11/XKBlib.h>
#elif defined Q_OS_MAC
#define __SECURITYHI__
#include <Carbon/Carbon.h>
#endif
#include <QApplication>
bool KeyboardState::checkModifiersPressed( int mask )
{
#if defined( Q_OS_WIN32 )
auto modifiers = QApplication::queryKeyboardModifiers();
return !( ( mask & Alt && !( GetAsyncKeyState( VK_MENU ) & 0x8000 ) )
|| ( mask & Ctrl && !( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) )
|| ( mask & Shift && !( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) )
|| ( mask & LeftAlt && !( GetAsyncKeyState( VK_LMENU ) & 0x8000 ) )
|| ( mask & RightAlt && !( GetAsyncKeyState( VK_RMENU ) & 0x8000 ) )
|| ( mask & LeftCtrl && !( GetAsyncKeyState( VK_LCONTROL ) & 0x8000 ) )
|| ( mask & RightCtrl && !( GetAsyncKeyState( VK_RCONTROL ) & 0x8000 ) )
|| ( mask & LeftShift && !( GetAsyncKeyState( VK_LSHIFT ) & 0x8000 ) )
|| ( mask & RightShift && !( GetAsyncKeyState( VK_RSHIFT ) & 0x8000 ) ) );
#elif defined Q_OS_MAC
UInt32 keys = GetCurrentKeyModifiers();
return !( ( mask & Alt && !( keys & ( 1 << optionKeyBit ) ) ) || ( mask & Ctrl && !( keys & ( 1 << cmdKeyBit ) ) )
|| ( mask & Shift && !( keys & ( 1 << shiftKeyBit ) ) )
|| ( mask & Win && !( keys & ( 1 << controlKeyBit ) ) ) );
#else
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
XkbStateRec state;
XkbGetState( displayID, XkbUseCoreKbd, &state );
return !( ( mask & Alt && !( state.base_mods & Mod1Mask ) ) || ( mask & Ctrl && !( state.base_mods & ControlMask ) )
|| ( mask & Shift && !( state.base_mods & ShiftMask ) )
|| ( mask & Win && !( state.base_mods & Mod4Mask ) ) );
#endif
return !( ( mask & Alt && !( modifiers.testFlag( Qt::AltModifier ) ) )
|| ( mask & Ctrl && !( modifiers.testFlag( Qt::ControlModifier ) ) )
|| ( mask & Shift && !( modifiers.testFlag( Qt::ShiftModifier ) ) ) );
}

View file

@ -15,13 +15,7 @@ public:
Alt = 1,
Ctrl = 2,
Shift = 4,
Win = 8, // Ironically, Linux only, since it's no use under Windows
LeftAlt = 16, // Those Left-Right are Windows-only, at least for now
RightAlt = 32,
LeftCtrl = 64,
RightCtrl = 128,
LeftShift = 256,
RightShift = 512
Win = 8, // Ironically, Linux only, since it's no use under Windows
};
/// Returns true if all Modifiers present within the given mask are pressed

View file

@ -478,7 +478,7 @@ void ArticleView::loadFinished( bool result )
void ArticleView::handleTitleChanged( QString const & title )
{
if ( !title.isEmpty() )
if ( !title.isEmpty() && !title.contains( "://" ) )
emit titleChanged( this, title );
}
@ -1443,7 +1443,10 @@ void ArticleView::setContent( const QByteArray & data, const QString & mimeType,
QString ArticleView::getTitle()
{
return webview->page()->title();
auto title = webview->title();
if ( title.contains( "://" ) )
return {};
return webview->title();
}
QString ArticleView::getWord() const
@ -2125,8 +2128,8 @@ void ArticleView::highlightFTSResults()
QString script = QString(
"var context = document.querySelector(\"body\");\n"
"var instance = new Mark(context);\n"
"instance.mark(\"%1\",{\"accuracy\": \"exactly\"});" )
"var instance = new Mark(context);\n instance.unmark();\n"
"instance.mark(\"%1\");" )
.arg( regString );
webview->page()->runJavaScript( script );

View file

@ -16,14 +16,12 @@
#include <QMutexLocker>
#include <memory>
#define AUTO_APPLY_LIMIT 150000
enum SearchType {
FixedString,
Wildcard,
Regex
};
DictHeadwords::DictHeadwords( QWidget * parent, Config::Class & cfg_, Dictionary::Class * dict_ ):
QDialog( parent ),
cfg( cfg_ ),
@ -55,6 +53,7 @@ DictHeadwords::DictHeadwords( QWidget * parent, Config::Class & cfg_, Dictionary
ui.matchCase->setChecked( cfg.headwordsDialog.matchCase );
model = std::make_shared< HeadwordListModel >();
model->setMaxFilterResults( ui.filterMaxResult->value() );
proxy = new QSortFilterProxyModel( this );
proxy->setSourceModel( model.get() );
@ -75,6 +74,10 @@ DictHeadwords::DictHeadwords( QWidget * parent, Config::Class & cfg_, Dictionary
ui.autoApply->setChecked( cfg.headwordsDialog.autoApply );
//arbitrary number.
bool exceedLimit = model->totalCount() > HEADWORDS_MAX_LIMIT;
ui.filterMaxResult->setEnabled( exceedLimit );
connect( this, &QDialog::finished, this, &DictHeadwords::savePos );
if ( !fromMainWindow ) {
@ -106,7 +109,6 @@ DictHeadwords::DictHeadwords( QWidget * parent, Config::Class & cfg_, Dictionary
connect( ui.headersListView, &QAbstractItemView::clicked, this, &DictHeadwords::itemClicked );
connect( proxy, &QAbstractItemModel::dataChanged, this, &DictHeadwords::showHeadwordsNumber );
ui.headersListView->installEventFilter( this );
@ -124,45 +126,46 @@ void DictHeadwords::setup( Dictionary::Class * dict_ )
QApplication::setOverrideCursor( Qt::WaitCursor );
dict = dict_;
sortedWords.clear();
setWindowTitle( QString::fromUtf8( dict->getName().c_str() ) );
const auto size = dict->getWordCount();
std::shared_ptr< HeadwordListModel > other = std::make_shared< HeadwordListModel >();
model.swap( other );
model->setMaxFilterResults( ui.filterMaxResult->value() );
model->setDict( dict );
proxy->setSourceModel( model.get() );
proxy->sort( 0 );
filterChanged();
if ( size > AUTO_APPLY_LIMIT ) {
cfg.headwordsDialog.autoApply = ui.autoApply->isChecked();
ui.autoApply->setChecked( false );
ui.autoApply->setEnabled( false );
}
else {
ui.autoApply->setEnabled( true );
ui.autoApply->setChecked( cfg.headwordsDialog.autoApply );
}
ui.autoApply->setEnabled( true );
ui.autoApply->setChecked( cfg.headwordsDialog.autoApply );
ui.applyButton->setEnabled( !ui.autoApply->isChecked() );
//arbitrary number.
bool exceedLimit = model->totalCount() > HEADWORDS_MAX_LIMIT;
ui.filterMaxResult->setEnabled( exceedLimit );
setWindowIcon( dict->getIcon() );
dictId = QString( dict->getId().c_str() );
QApplication::restoreOverrideCursor();
connect( model.get(), &HeadwordListModel::numberPopulated, this, [ this ]( int _ ) {
showHeadwordsNumber();
} );
connect( proxy, &QAbstractItemModel::dataChanged, this, &DictHeadwords::showHeadwordsNumber );
connect( ui.filterMaxResult, &QSpinBox::valueChanged, this, [ this ]( int _value ) {
model->setMaxFilterResults( _value );
} );
}
void DictHeadwords::savePos()
{
cfg.headwordsDialog.searchMode = ui.searchModeCombo->currentIndex();
cfg.headwordsDialog.matchCase = ui.matchCase->isChecked();
if ( model->totalCount() <= AUTO_APPLY_LIMIT )
cfg.headwordsDialog.autoApply = ui.autoApply->isChecked();
cfg.headwordsDialog.autoApply = ui.autoApply->isChecked();
cfg.headwordsDialog.headwordsDialogGeometry = saveGeometry();
}
@ -255,18 +258,7 @@ void DictHeadwords::filterChanged()
QApplication::setOverrideCursor( Qt::WaitCursor );
if ( sortedWords.isEmpty() && regExp.isValid() && !regExp.pattern().isEmpty() ) {
const int headwordsNumber = model->totalCount();
QProgressDialog progress( tr( "Loading headwords..." ), tr( "Cancel" ), 0, headwordsNumber, this );
progress.setWindowModality( Qt::WindowModal );
loadAllSortedWords( progress );
progress.close();
}
const auto filtered = sortedWords.filter( regExp );
model->addMatches( filtered );
model->setFilter( regExp );
proxy->setFilterRegularExpression( regExp );
proxy->sort( 0 );
@ -292,30 +284,44 @@ void DictHeadwords::autoApplyStateChanged( int state )
void DictHeadwords::showHeadwordsNumber()
{
ui.headersNumber->setText( tr( "Unique headwords total: %1, filtered: %2" )
.arg( QString::number( model->totalCount() ), QString::number( proxy->rowCount() ) ) );
if ( ui.filterLine->text().isEmpty() ) {
ui.headersNumber->setText( tr( "Unique headwords total: %1." ).arg( QString::number( model->totalCount() ) ) );
}
else {
ui.headersNumber->setText( tr( "Unique headwords total: %1, filtered(limited): %2" )
.arg( QString::number( model->totalCount() ), QString::number( proxy->rowCount() ) ) );
}
}
void DictHeadwords::loadAllSortedWords( QProgressDialog & progress )
void DictHeadwords::exportAllWords( QProgressDialog & progress, QTextStream & out )
{
if ( const QRegularExpression regExp = getFilterRegex(); regExp.isValid() && !regExp.pattern().isEmpty() ) {
loadRegex( progress, out );
return;
}
const int headwordsNumber = model->totalCount();
QMutexLocker const _( &mutex );
if ( sortedWords.isEmpty() ) {
QSet< QString > allHeadwords;
QSet< QString > allHeadwords;
int totalCount = 0;
for ( int i = 0; i < headwordsNumber && i < model->wordCount(); ++i ) {
if ( progress.wasCanceled() )
break;
progress.setValue( totalCount++ );
int totalCount = 0;
for ( int i = 0; i < headwordsNumber && i < model->wordCount(); ++i ) {
if ( progress.wasCanceled() )
break;
QVariant value = model->getRow( i );
if ( !value.canConvert< QString >() )
continue;
QVariant value = model->getRow( i );
if ( !value.canConvert< QString >() )
continue;
allHeadwords.insert( value.toString() );
}
allHeadwords.insert( value.toString() );
}
for ( const auto & item : allHeadwords ) {
progress.setValue( totalCount++ );
writeWordToFile( out, item );
}
// continue to write the remaining headword
int nodeIndex = model->getCurrentIndex();
@ -323,16 +329,42 @@ void DictHeadwords::loadAllSortedWords( QProgressDialog & progress )
while ( !headwords.isEmpty() ) {
if ( progress.wasCanceled() )
break;
allHeadwords.unite( headwords );
totalCount += headwords.size();
progress.setValue( totalCount );
for ( const auto & item : headwords ) {
progress.setValue( totalCount++ );
writeWordToFile( out, item );
}
headwords = model->getRemainRows( nodeIndex );
}
}
sortedWords = allHeadwords.values();
sortedWords.sort();
void DictHeadwords::loadRegex( QProgressDialog & progress, QTextStream & out )
{
const int headwordsNumber = model->totalCount();
QMutexLocker const _( &mutex );
QSet< QString > allHeadwords;
int totalCount = 0;
for ( int i = 0; i < model->wordCount(); ++i ) {
if ( progress.wasCanceled() )
break;
QVariant value = model->getRow( i );
if ( !value.canConvert< QString >() )
continue;
allHeadwords.insert( value.toString() );
}
progress.setMaximum( allHeadwords.size() );
for ( const auto & item : allHeadwords ) {
progress.setValue( totalCount++ );
writeWordToFile( out, item );
}
}
@ -366,11 +398,9 @@ void DictHeadwords::saveHeadersToFile()
const int headwordsNumber = model->totalCount();
QProgressDialog progress( tr( "Export headwords..." ), tr( "Cancel" ), 0, headwordsNumber * 2, this );
QProgressDialog progress( tr( "Export headwords..." ), tr( "Cancel" ), 0, headwordsNumber, this );
progress.setWindowModality( Qt::WindowModal );
loadAllSortedWords( progress );
// Write UTF-8 BOM
QTextStream out( &file );
out.setGenerateByteOrderMark( true );
@ -379,29 +409,7 @@ void DictHeadwords::saveHeadersToFile()
out.setCodec( "UTF-8" );
#endif
QStringList filtered;
if ( const QRegularExpression regExp = getFilterRegex(); regExp.isValid() && !regExp.pattern().isEmpty() ) {
filtered = sortedWords.filter( regExp );
}
else {
filtered = sortedWords;
}
auto currentValue = progress.value();
// Write headwords
for ( auto const & word : filtered ) {
if ( progress.wasCanceled() )
break;
progress.setValue( currentValue++ );
QByteArray line = word.toUtf8();
//usually the headword should not contain \r \n;
line.replace( '\n', ' ' );
line.replace( '\r', ' ' );
//write one line
out << line << Qt::endl;
}
exportAllWords( progress, out );
file.close();
@ -412,8 +420,18 @@ void DictHeadwords::saveHeadersToFile()
}
else {
//completed.
progress.setValue( headwordsNumber * 2 );
progress.close();
QMessageBox::information( this, "GoldenDict", tr( "Export finished" ) );
}
}
void DictHeadwords::writeWordToFile( QTextStream & out, const QString & word )
{
QByteArray line = word.toUtf8();
//usually the headword should not contain \r \n;
line.replace( '\n', ' ' );
line.replace( '\r', ' ' );
//write one line
out << line << Qt::endl;
}

View file

@ -42,8 +42,10 @@ protected:
private:
Ui::DictHeadwords ui;
QStringList sortedWords;
// QStringList sortedWords;
QMutex mutex;
static void writeWordToFile( QTextStream & out, const QString & word );
private slots:
void savePos();
void filterChangedInternal();
@ -54,7 +56,8 @@ private slots:
void itemClicked( const QModelIndex & index );
void autoApplyStateChanged( int state );
void showHeadwordsNumber();
void loadAllSortedWords( QProgressDialog & progress );
void exportAllWords( QProgressDialog & progress, QTextStream & out );
void loadRegex( QProgressDialog & progress, QTextStream & out );
virtual void reject();
signals:

View file

@ -101,6 +101,32 @@
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>Specify the maximum filtered headwords returned.</string>
</property>
<property name="text">
<string>Filter max results:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="filterMaxResult">
<property name="toolTip">
<string/>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>3000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>

View file

@ -27,18 +27,6 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
connect( ui.showScanFlag, &QAbstractButton::toggled, this, &Preferences::showScanFlagToggled );
connect( ui.altKey, &QAbstractButton::clicked, this, &Preferences::wholeAltClicked );
connect( ui.ctrlKey, &QAbstractButton::clicked, this, &Preferences::wholeCtrlClicked );
connect( ui.shiftKey, &QAbstractButton::clicked, this, &Preferences::wholeShiftClicked );
connect( ui.leftAlt, &QAbstractButton::clicked, this, &Preferences::sideAltClicked );
connect( ui.rightAlt, &QAbstractButton::clicked, this, &Preferences::sideAltClicked );
connect( ui.leftCtrl, &QAbstractButton::clicked, this, &Preferences::sideCtrlClicked );
connect( ui.rightCtrl, &QAbstractButton::clicked, this, &Preferences::sideCtrlClicked );
connect( ui.leftShift, &QAbstractButton::clicked, this, &Preferences::sideShiftClicked );
connect( ui.rightShift, &QAbstractButton::clicked, this, &Preferences::sideShiftClicked );
helpAction.setShortcut( QKeySequence( "F1" ) );
helpAction.setShortcutContext( Qt::WidgetWithChildrenShortcut );
@ -206,12 +194,6 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
ui.ctrlKey->setChecked( p.scanPopupModifiers & KeyboardState::Ctrl );
ui.shiftKey->setChecked( p.scanPopupModifiers & KeyboardState::Shift );
ui.winKey->setChecked( p.scanPopupModifiers & KeyboardState::Win );
ui.leftAlt->setChecked( p.scanPopupModifiers & KeyboardState::LeftAlt );
ui.rightAlt->setChecked( p.scanPopupModifiers & KeyboardState::RightAlt );
ui.leftCtrl->setChecked( p.scanPopupModifiers & KeyboardState::LeftCtrl );
ui.rightCtrl->setChecked( p.scanPopupModifiers & KeyboardState::RightCtrl );
ui.leftShift->setChecked( p.scanPopupModifiers & KeyboardState::LeftShift );
ui.rightShift->setChecked( p.scanPopupModifiers & KeyboardState::RightShift );
ui.ignoreOwnClipboardChanges->setChecked( p.ignoreOwnClipboardChanges );
ui.scanToMainWindow->setChecked( p.scanToMainWindow );
@ -250,12 +232,6 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
#ifdef Q_OS_WIN32
ui.winKey->hide();
#else
ui.leftAlt->hide();
ui.rightAlt->hide();
ui.leftCtrl->hide();
ui.rightCtrl->hide();
ui.leftShift->hide();
ui.rightShift->hide();
#ifdef Q_OS_MAC
ui.altKey->setText( "Opt" );
ui.winKey->setText( "Ctrl" );
@ -446,12 +422,6 @@ Config::Preferences Preferences::getPreferences()
p.scanPopupModifiers += ui.ctrlKey->isChecked() ? KeyboardState::Ctrl : 0;
p.scanPopupModifiers += ui.shiftKey->isChecked() ? KeyboardState::Shift : 0;
p.scanPopupModifiers += ui.winKey->isChecked() ? KeyboardState::Win : 0;
p.scanPopupModifiers += ui.leftAlt->isChecked() ? KeyboardState::LeftAlt : 0;
p.scanPopupModifiers += ui.rightAlt->isChecked() ? KeyboardState::RightAlt : 0;
p.scanPopupModifiers += ui.leftCtrl->isChecked() ? KeyboardState::LeftCtrl : 0;
p.scanPopupModifiers += ui.rightCtrl->isChecked() ? KeyboardState::RightCtrl : 0;
p.scanPopupModifiers += ui.leftShift->isChecked() ? KeyboardState::LeftShift : 0;
p.scanPopupModifiers += ui.rightShift->isChecked() ? KeyboardState::RightShift : 0;
p.ignoreOwnClipboardChanges = ui.ignoreOwnClipboardChanges->isChecked();
p.scanToMainWindow = ui.scanToMainWindow->isChecked();
@ -556,48 +526,6 @@ void Preferences::showScanFlagToggled( bool b )
ui.enableScanPopupModifiers->setChecked( false );
}
void Preferences::wholeAltClicked( bool b )
{
if ( b ) {
ui.leftAlt->setChecked( false );
ui.rightAlt->setChecked( false );
}
}
void Preferences::wholeCtrlClicked( bool b )
{
if ( b ) {
ui.leftCtrl->setChecked( false );
ui.rightCtrl->setChecked( false );
}
}
void Preferences::wholeShiftClicked( bool b )
{
if ( b ) {
ui.leftShift->setChecked( false );
ui.rightShift->setChecked( false );
}
}
void Preferences::sideAltClicked( bool )
{
if ( ui.leftAlt->isChecked() || ui.rightAlt->isChecked() )
ui.altKey->setChecked( false );
}
void Preferences::sideCtrlClicked( bool )
{
if ( ui.leftCtrl->isChecked() || ui.rightCtrl->isChecked() )
ui.ctrlKey->setChecked( false );
}
void Preferences::sideShiftClicked( bool )
{
if ( ui.leftShift->isChecked() || ui.rightShift->isChecked() )
ui.shiftKey->setChecked( false );
}
void Preferences::on_enableMainWindowHotkey_toggled( bool checked )
{
ui.mainWindowHotkey->setEnabled( checked );

View file

@ -41,14 +41,6 @@ private slots:
void enableScanPopupModifiersToggled( bool );
void showScanFlagToggled( bool b );
void wholeAltClicked( bool );
void wholeCtrlClicked( bool );
void wholeShiftClicked( bool );
void sideAltClicked( bool );
void sideCtrlClicked( bool );
void sideShiftClicked( bool );
void on_enableMainWindowHotkey_toggled( bool checked );
void on_enableClipboardHotkey_toggled( bool checked );

View file

@ -621,26 +621,6 @@ in the pressed state when the word selection changes.</string>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QCheckBox" name="leftCtrl">
<property name="toolTip">
<string>Left Ctrl only</string>
</property>
<property name="text">
<string>Left Ctrl</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="rightShift">
<property name="toolTip">
<string>Right Shift only</string>
</property>
<property name="text">
<string>Right Shift</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="altKey">
<property name="toolTip">
@ -661,16 +641,6 @@ in the pressed state when the word selection changes.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="leftAlt">
<property name="toolTip">
<string>Left Alt only</string>
</property>
<property name="text">
<string>Left Alt</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="shiftKey">
<property name="toolTip">
@ -681,36 +651,6 @@ in the pressed state when the word selection changes.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="rightAlt">
<property name="toolTip">
<string>Right Alt only</string>
</property>
<property name="text">
<string>Right Alt</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="rightCtrl">
<property name="toolTip">
<string>Right Ctrl only</string>
</property>
<property name="text">
<string>Right Ctrl</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="leftShift">
<property name="toolTip">
<string>Left Shift only</string>
</property>
<property name="text">
<string>Left Shift</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="winKey">
<property name="toolTip">
@ -2035,15 +1975,9 @@ from Stardict, Babylon and GLS dictionaries</string>
<tabstop>startToTray</tabstop>
<tabstop>closeToTray</tabstop>
<tabstop>cbAutostart</tabstop>
<tabstop>leftCtrl</tabstop>
<tabstop>rightShift</tabstop>
<tabstop>altKey</tabstop>
<tabstop>ctrlKey</tabstop>
<tabstop>leftAlt</tabstop>
<tabstop>shiftKey</tabstop>
<tabstop>rightAlt</tabstop>
<tabstop>rightCtrl</tabstop>
<tabstop>leftShift</tabstop>
<tabstop>winKey</tabstop>
<tabstop>useProxyServer</tabstop>
<tabstop>proxyType</tabstop>