Show all headwords for selected dictionary

This commit is contained in:
Abs62 2014-02-28 16:36:28 +04:00
parent becc74c730
commit 4a4bfade1d
13 changed files with 756 additions and 48 deletions

View file

@ -38,6 +38,7 @@ namespace BtreeIndexing {
using gd::wstring;
using gd::wchar;
using std::pair;
enum
{
@ -709,8 +710,6 @@ vector< WordArticleLink > BtreeIndex::readChain( char const * & ptr )
vector< WordArticleLink > result;
vector< char > charBuffer;
while( chainSize )
{
string str = ptr;
@ -1073,4 +1072,128 @@ IndexInfo buildIndex( IndexedWords const & indexedWords, File::Class & file )
return IndexInfo( btreeMaxElements, rootOffset );
}
void BtreeIndex::getAllHeadwords( QSet< QString > & headwords )
{
if ( !idxFile )
throw exIndexWasNotOpened();
Mutex::Lock _( *idxFileMutex );
findNodeArticleLinks( rootOffset, NULL, NULL, &headwords );
}
void BtreeIndex::findAllArticleLinks( QVector< FTSLink > & articleLinks )
{
if ( !idxFile )
throw exIndexWasNotOpened();
Mutex::Lock _( *idxFileMutex );
QSet< uint32_t > offsets;
findNodeArticleLinks( rootOffset, &articleLinks, &offsets, NULL );
}
void BtreeIndex::findNodeArticleLinks( uint32_t currentNodeOffset,
QVector< FTSLink > * articleLinks,
QSet< uint32_t > * offsets,
QSet< QString > * headwords )
{
// Read a node
vector< char > extLeaf;
if( rootNodeLoaded && currentNodeOffset == rootOffset )
extLeaf = rootNode;
else
readNode( currentNodeOffset, extLeaf );
char const * leaf = &extLeaf.front();
// Is it a leaf or a node?
uint32_t leafEntries = *(uint32_t *)leaf;
if ( leafEntries == 0xffffFFFF )
{
// A node
uint32_t const * offs = (uint32_t *)leaf + 1;
for( unsigned i = 0; i <= indexNodeSize; i++ )
findNodeArticleLinks( offs[ i ], articleLinks, offsets, headwords );
}
else
{
// A leaf
if ( !leafEntries )
{
// Empty leaf? This may only be possible for entirely empty trees only.
if ( currentNodeOffset != rootOffset )
throw exCorruptedChainData();
else
return; // No match
}
// Build an array containing all chain pointers
char const * ptr = leaf + sizeof( uint32_t );
uint32_t chainSize;
QPair< QSet< uint32_t >::iterator, bool > pr;
while( leafEntries-- )
{
memcpy( &chainSize, ptr, sizeof( uint32_t ) );
char const * chainPtr = ptr;
vector< WordArticleLink > result = readChain( chainPtr );
for( unsigned i = 0; i < result.size(); i++ )
{
if( headwords )
headwords->insert( QString::fromUtf8( ( result[ i ].prefix + result[ i ].word ).c_str() ) );
if( !offsets || offsets->contains( result[ i ].articleOffset ) )
continue;
offsets->insert( result[ i ].articleOffset );
if( articleLinks )
articleLinks->push_back( FTSLink( result[ i ].prefix + result[ i ].word, result[ i ].articleOffset ) );
}
ptr += sizeof( uint32_t ) + chainSize;
}
}
}
bool BtreeDictionary::getHeadwords( QStringList &headwords )
{
QSet< QString > setOfHeadwords;
headwords.clear();
try
{
getAllHeadwords( setOfHeadwords );
if( setOfHeadwords.size() )
{
headwords.reserve( setOfHeadwords.size() );
for( QSet< QString >::const_iterator it = setOfHeadwords.constBegin();
it != setOfHeadwords.constEnd(); ++it )
{
headwords.append( *it );
}
headwords.sort();
}
}
catch( std::exception &ex )
{
gdWarning( "Failed headwords retrieving for \"%s\", reason: %s\n", getName().c_str(), ex.what() );
}
return headwords.size() > 0;
}
}

View file

@ -10,6 +10,8 @@
#include <string>
#include <vector>
#include <map>
#include <QVector>
#include <QSet>
#ifdef _MSC_VER
#include <stdint_msvc.h>
@ -55,6 +57,21 @@ struct WordArticleLink
{}
};
/// Links for full-text search
struct FTSLink
{
string word; // in utf8
uint32_t articleOffset;
uint32_t linkOffset;
FTSLink()
{}
FTSLink( string const & word_, uint32_t articleOffset_ ):
word( word_ ), articleOffset( articleOffset_ ), linkOffset( 0 )
{}
};
/// Information needed to open the index
struct IndexInfo
{
@ -83,6 +100,11 @@ public:
/// is performed.
vector< WordArticleLink > findArticles( wstring const & );
/// Find all chain offsets in the index
void findAllArticleLinks( QVector< FTSLink > & articleLinks );
/// Retrieve all unique headwors from index
void getAllHeadwords( QSet< QString > & headwords );
protected:
/// Finds the offset in the btree leaf for the given word, either matching
@ -115,6 +137,11 @@ protected:
/// are left.
void antialias( wstring const &, vector< WordArticleLink > & );
/// Find all article links for node
void findNodeArticleLinks( uint32_t currentNodeOffset,
QVector< FTSLink > * articleLinks,
QSet< uint32_t > * offsets,
QSet< QString > * headwords );
protected:
Mutex * idxFileMutex;
@ -158,6 +185,8 @@ public:
virtual bool isLocalDictionary()
{ return true; }
virtual bool getHeadwords( QStringList &headwords );
protected:
/// Called before each matching operation to ensure that any child init

View file

@ -870,6 +870,26 @@ Class load() throw( exError )
}
}
QDomNode headwordsDialog = root.namedItem( "headwordsDialog" );
if ( !headwordsDialog.isNull() )
{
if ( !headwordsDialog.namedItem( "searchMode" ).isNull() )
c.headwordsDialog.searchMode = headwordsDialog.namedItem( "searchMode" ).toElement().text().toInt() ;
if ( !headwordsDialog.namedItem( "matchCase" ).isNull() )
c.headwordsDialog.matchCase = ( headwordsDialog.namedItem( "matchCase" ).toElement().text() == "1" );
if ( !headwordsDialog.namedItem( "autoApply" ).isNull() )
c.headwordsDialog.autoApply = ( headwordsDialog.namedItem( "autoApply" ).toElement().text() == "1" );
if ( !headwordsDialog.namedItem( "headwordsExportPath" ).isNull() )
c.headwordsDialog.headwordsExportPath = headwordsDialog.namedItem( "headwordsExportPath" ).toElement().text();
if ( !headwordsDialog.namedItem( "headwordsDialogGeometry" ).isNull() )
c.headwordsDialog.headwordsDialogGeometry = QByteArray::fromBase64( headwordsDialog.namedItem( "headwordsDialogGeometry" ).toElement().text().toLatin1() );
}
return c;
}
@ -1646,6 +1666,31 @@ void save( Class const & c ) throw( exError )
root.appendChild( opt );
}
{
QDomNode hd = dd.createElement( "headwordsDialog" );
root.appendChild( hd );
QDomElement opt = dd.createElement( "searchMode" );
opt.appendChild( dd.createTextNode( QString::number( c.headwordsDialog.searchMode ) ) );
hd.appendChild( opt );
opt = dd.createElement( "matchCase" );
opt.appendChild( dd.createTextNode( c.headwordsDialog.matchCase ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "autoApply" );
opt.appendChild( dd.createTextNode( c.headwordsDialog.autoApply ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "headwordsExportPath" );
opt.appendChild( dd.createTextNode( c.headwordsDialog.headwordsExportPath ) );
hd.appendChild( opt );
opt = dd.createElement( "headwordsDialogGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.headwordsDialog.headwordsDialogGeometry.toBase64() ) ) );
hd.appendChild( opt );
}
QByteArray result( dd.toByteArray() );
if ( configFile.write( result ) != result.size() )

View file

@ -426,6 +426,20 @@ struct VoiceEngine
typedef QVector< VoiceEngine> VoiceEngines;
struct HeadwordsDialog
{
int searchMode;
bool matchCase;
bool autoApply;
QString headwordsExportPath;
QByteArray headwordsDialogGeometry;
HeadwordsDialog() :
searchMode( 0 ), matchCase( false )
, autoApply( false )
{}
};
struct Class
{
Paths paths;
@ -476,6 +490,8 @@ struct Class
/// Bigger headwords won't be indexed. For now, only in DSL.
unsigned int maxHeadwordSize;
HeadwordsDialog headwordsDialog;
#ifdef Q_OS_WIN
QRect maximizedMainWindowGeometry;
QRect normalMainWindowGeometry;

255
dictheadwords.cc Normal file
View file

@ -0,0 +1,255 @@
/* This file is (c) 2014 Abs62
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "dictheadwords.hh"
#include "gddebug.hh"
#include <QRegExp>
#include <QDir>
#include <QFileDialog>
#include <QTimer>
#include <QProgressDialog>
#define AUTO_APPLY_LIMIT 150000
DictHeadwords::DictHeadwords( QWidget *parent, Config::Class & cfg_,
Dictionary::Class * dict_ ) :
QDialog(parent)
, cfg( cfg_ )
, dict( dict_ )
{
QApplication::setOverrideCursor( Qt::WaitCursor );
ui.setupUi( this );
if( cfg.headwordsDialog.headwordsDialogGeometry.size() > 0 )
restoreGeometry( cfg.headwordsDialog.headwordsDialogGeometry );
setWindowTitle( QString::fromUtf8( dict->getName().c_str() ) );
ui.searchModeCombo->addItem( tr( "Text" ), QRegExp::FixedString );
ui.searchModeCombo->addItem( tr( "Wildcards" ), QRegExp::Wildcard );
ui.searchModeCombo->addItem( tr( "RegExp" ), QRegExp::RegExp );
ui.searchModeCombo->setCurrentIndex( cfg.headwordsDialog.searchMode );
ui.exportButton->setAutoDefault( false );
ui.OKButton->setAutoDefault( false);
ui.applyButton->setAutoDefault( true );
ui.applyButton->setDefault( true );
ui.matchCase->setChecked( cfg.headwordsDialog.matchCase );
dict->getHeadwords( headers );
model = new QStringListModel( this );
model->setStringList( headers );
proxy = new QSortFilterProxyModel( this );
proxy->setSourceModel( model );
// proxy->setDynamicSortFilter( true );
ui.headersListView->setModel( proxy );
// very important call, for performance reasons:
ui.headersListView->setUniformItemSizes( true );
delegate = new WordListItemDelegate( ui.headersListView->itemDelegate() );
if( delegate )
ui.headersListView->setItemDelegate( delegate );
if( headers.size() > AUTO_APPLY_LIMIT )
{
ui.autoApply->setChecked( false );
ui.autoApply->setEnabled( false );
}
else
{
ui.autoApply->setChecked( cfg.headwordsDialog.autoApply );
ui.applyButton->setEnabled( !cfg.headwordsDialog.autoApply );
}
connect( this, SIGNAL( finished( int ) ), this, SLOT( savePos( int ) ) );
connect( ui.OKButton, SIGNAL( clicked( bool ) ), this, SLOT( okButtonClicked() ) );
connect( ui.exportButton, SIGNAL( clicked( bool ) ), this, SLOT( exportButtonClicked() ) );
connect( ui.applyButton, SIGNAL( clicked( bool ) ), this, SLOT( filterChanged() ) );
connect( ui.autoApply, SIGNAL( stateChanged( int ) ),
this, SLOT( autoApplyStateChanged( int ) ) );
connect( ui.filterLine, SIGNAL( textChanged( QString ) ),
this, SLOT( filterChangedInternal() ) );
connect( ui.searchModeCombo, SIGNAL( currentIndexChanged( int ) ),
this, SLOT( filterChangedInternal() ) );
connect( ui.matchCase, SIGNAL( stateChanged( int ) ),
this, SLOT( filterChangedInternal() ) );
connect( ui.headersListView, SIGNAL( clicked( QModelIndex ) ),
this, SLOT( itemClicked( QModelIndex ) ) );
connect( proxy, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
this, SLOT( showHeadwordsNumber() ) );
showHeadwordsNumber();
QApplication::setOverrideCursor( Qt::ArrowCursor );
}
DictHeadwords::~DictHeadwords()
{
if( delegate )
delete delegate;
}
void DictHeadwords::savePos( int )
{
cfg.headwordsDialog.searchMode = ui.searchModeCombo->currentIndex();
cfg.headwordsDialog.matchCase = ui.matchCase->isChecked();
if( headers.size() <= AUTO_APPLY_LIMIT )
cfg.headwordsDialog.autoApply = ui.autoApply->isChecked();
cfg.headwordsDialog.headwordsDialogGeometry = saveGeometry();
}
void DictHeadwords::okButtonClicked()
{
done( 0 );
}
void DictHeadwords::exportButtonClicked()
{
saveHeadersToFile();
}
void DictHeadwords::filterChangedInternal()
{
// emit signal in async manner, to avoid UI slowdown
if( ui.autoApply->isChecked() )
QTimer::singleShot( 100, this, SLOT( filterChanged() ) );
}
void DictHeadwords::filterChanged()
{
QRegExp::PatternSyntax syntax =
QRegExp::PatternSyntax( ui.searchModeCombo->itemData(
ui.searchModeCombo->currentIndex()).toInt() );
Qt::CaseSensitivity caseSensitivity = ui.matchCase->isChecked() ? Qt::CaseSensitive
: Qt::CaseInsensitive;
QRegExp regExp( ui.filterLine->text(), caseSensitivity, syntax );
QApplication::setOverrideCursor( Qt::WaitCursor );
proxy->setFilterRegExp( regExp );
QApplication::setOverrideCursor( Qt::ArrowCursor );
showHeadwordsNumber();
}
void DictHeadwords::itemClicked( const QModelIndex & index )
{
QVariant value = proxy->data( index, Qt::DisplayRole );
if ( value.canConvert< QString >() )
emit headwordSelected( value.toString() );
}
void DictHeadwords::autoApplyStateChanged( int state )
{
ui.applyButton->setEnabled( state == Qt::Unchecked );
}
void DictHeadwords::showHeadwordsNumber()
{
ui.headersNumber->setText( tr( "Unique headwords total: %1, filtered: %2" )
.arg( QString::number( headers.size() ) )
.arg( QString::number( proxy->rowCount() ) ) );
}
void DictHeadwords::saveHeadersToFile()
{
QString exportPath;
if( cfg.headwordsDialog.headwordsExportPath.isEmpty() )
exportPath = QDir::homePath();
else
{
exportPath = QDir::fromNativeSeparators( cfg.headwordsDialog.headwordsExportPath );
if( !QDir( exportPath ).exists() )
exportPath = QDir::homePath();
}
QString fileName = QFileDialog::getSaveFileName( this, tr( "Save headwords to file" ),
exportPath,
tr( "Text files (*.txt);;All files (*.*)" ) );
if( fileName.size() == 0)
return;
cfg.headwordsDialog.headwordsExportPath = QDir::toNativeSeparators(
QFileInfo( fileName ).absoluteDir().absolutePath() );
QFile file( fileName );
for(;;)
{
if ( !file.open( QFile::WriteOnly | QIODevice::Text ) )
break;
int headwordsNumber = proxy->rowCount();
// Setup progress dialog
int n = headwordsNumber;
int step = 1;
while( n > 1000 )
{
step *= 10;
n /= 10;
}
QProgressDialog progress( tr( "Export headwords..."), tr( "Cancel" ),
0, n, this );
progress.setWindowModality( Qt::WindowModal );
// Write UTF-8 BOM
QByteArray line;
line.append( 0xEF ).append( 0xBB ).append( 0xBF );
if ( file.write( line ) != line.size() )
break;
// Write headwords
int i;
for( i = 0; i < headwordsNumber; ++i )
{
if( i % step == 0 )
progress.setValue( i / step );
if( progress.wasCanceled() )
break;
QVariant value = proxy->data( proxy->index( i, 0 ) );
if( !value.canConvert< QString >() )
continue;
line = value.toString().toUtf8();
line.replace( '\n', ' ' );
line.replace( '\r', ' ' );
line += "\n";
if ( file.write( line ) != line.size() )
break;
}
if( i < headwordsNumber && !progress.wasCanceled() )
break;
file.close();
return;
}
gdWarning( "Headers export error: %s", file.errorString().toUtf8().data() );
file.close();
}

53
dictheadwords.hh Normal file
View file

@ -0,0 +1,53 @@
#ifndef __DICTHEADWORDS_H_INCLUDED__
#define __DICTHEADWORDS_H_INCLUDED__
#include <QDialog>
#include <QSet>
#include <QString>
#include <QStringList>
#include <QStringListModel>
#include <QSortFilterProxyModel>
#include "config.hh"
#include "ui_dictheadwords.h"
#include "dictionary.hh"
#include "delegate.hh"
class DictHeadwords : public QDialog
{
Q_OBJECT
public:
explicit DictHeadwords( QWidget * parent, Config::Class & cfg_,
Dictionary::Class * dict_ );
virtual ~DictHeadwords();
void setup();
protected:
Config::Class & cfg;
Dictionary::Class * dict;
QStringList headers;
QStringListModel * model;
QSortFilterProxyModel * proxy;
WordListItemDelegate * delegate;
void saveHeadersToFile();
private:
Ui::DictHeadwords ui;
private slots:
void savePos( int );
void filterChangedInternal();
void filterChanged();
void exportButtonClicked();
void okButtonClicked();
void itemClicked( const QModelIndex & index );
void autoApplyStateChanged( int state );
void showHeadwordsNumber();
signals:
void headwordSelected( QString const & );
};
#endif // __DICTHEADWORDS_H_INCLUDED__

141
dictheadwords.ui Normal file
View file

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DictHeadwords</class>
<widget class="QDialog" name="DictHeadwords">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>458</width>
<height>550</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<widget class="QListView" name="headersListView"/>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Search mode</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="searchModeCombo">
<property name="toolTip">
<string>This element determines how filter string will be interpreted</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="matchCase">
<property name="toolTip">
<string>If checked on the symbols case will be take in account when filtering</string>
</property>
<property name="text">
<string>Match case</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="exportButton">
<property name="toolTip">
<string>Expors headwords to file</string>
</property>
<property name="text">
<string>Export</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="OKButton">
<property name="text">
<string>OK</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="applyButton">
<property name="toolTip">
<string>Press this button to apply filter to headwords list</string>
</property>
<property name="text">
<string>Apply</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="autoApply">
<property name="toolTip">
<string>If checked any filter changes will we immediately applied to headwords list</string>
</property>
<property name="text">
<string>Auto apply</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Filter:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="filterLine">
<property name="toolTip">
<string>Filter string (fixed string, wildcards or regular expression)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="headersNumber">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -28,6 +28,11 @@ void DictInfo::showInfo( sptr<Dictionary::Class> dict )
ui.editDictionary->setToolTip(
tr( "Edit the dictionary via command:\n%1" ).arg( cfg.editDictionaryCommandLine ) );
if( dict->getWordCount() == 0 )
ui.headwordsButton->setVisible( false );
else
ui.buttonsLayout->insertSpacerItem( 0, new QSpacerItem( 40, 20, QSizePolicy::Expanding ) );
std::vector< std::string > const & filenames = dict->getDictionaryFilenames();
QString filenamesText;
@ -62,3 +67,13 @@ void DictInfo::on_openFolder_clicked()
{
done( OPEN_FOLDER );
}
void DictInfo::on_OKButton_clicked()
{
done( ACCEPTED );
}
void DictInfo::on_headwordsButton_clicked()
{
done( SHOW_HEADWORDS );
}

View file

@ -16,7 +16,8 @@ public:
REJECTED,
ACCEPTED,
OPEN_FOLDER,
EDIT_DICTIONARY
EDIT_DICTIONARY,
SHOW_HEADWORDS
};
DictInfo( Config::Class &cfg_, QWidget * parent = 0 );
@ -29,6 +30,8 @@ private slots:
void savePos( int );
void on_editDictionary_clicked();
void on_openFolder_clicked();
void on_OKButton_clicked();
void on_headwordsButton_clicked();
};
#endif // DICTINFO_HH

View file

@ -256,50 +256,60 @@
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
<layout class="QHBoxLayout" name="buttonsLayout">
<item>
<widget class="QPushButton" name="headwordsButton">
<property name="toolTip">
<string>Show all unique dictionary headwords</string>
</property>
<property name="text">
<string>Headwords</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="OKButton">
<property name="text">
<string notr="true">OK</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DictInfo</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>180</x>
<y>379</y>
</hint>
<hint type="destinationlabel">
<x>25</x>
<y>791</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DictInfo</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>199</x>
<y>378</y>
</hint>
<hint type="destinationlabel">
<x>199</x>
<y>199</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

View file

@ -9,6 +9,7 @@
#include <map>
#include <QObject>
#include <QIcon>
#include <QStringList>
#include "sptr.hh"
#include "ex.hh"
#include "mutex.hh"
@ -400,6 +401,10 @@ public:
virtual bool isLocalDictionary()
{ return false; }
/// Retrieve all dictionary headwords
virtual bool getHeadwords( QStringList & )
{ return false; }
virtual ~Class()
{}
};

View file

@ -281,7 +281,8 @@ HEADERS += folding.hh \
zim.hh \
gddebug.hh \
gestures.hh \
tiff.hh
tiff.hh \
dictheadwords.hh
FORMS += groups.ui \
dictgroupwidget.ui \
@ -295,7 +296,8 @@ FORMS += groups.ui \
about.ui \
editdictionaries.ui \
orderandprops.ui \
dictinfo.ui
dictinfo.ui \
dictheadwords.ui
SOURCES += folding.cc \
main.cc \
dictionary.cc \
@ -394,7 +396,8 @@ SOURCES += folding.cc \
zim.cc \
gddebug.cc \
gestures.cc \
tiff.cc
tiff.cc \
dictheadwords.cc
win32 {
FORMS += texttospeechsource.ui

View file

@ -8,6 +8,7 @@
#include "about.hh"
#include "mruqmenu.hh"
#include "gestures.hh"
#include "dictheadwords.hh"
#include <limits.h>
#include <QDir>
#include <QMessageBox>
@ -3556,6 +3557,15 @@ void MainWindow::showDictionaryInfo( const QString & id )
{
editDictionary( dictionaries[x].get() );
}
else if( result == DictInfo::SHOW_HEADWORDS )
{
DictHeadwords headwordsDlg( this, cfg, dictionaries[ x ].get() );
connect( &headwordsDlg, SIGNAL( headwordSelected( QString ) ),
this, SLOT( wordReceived( QString ) ) );
headwordsDlg.exec();
}
break;
}