mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-24 00:14:06 +00:00
Add new program type - 'Prefix Match', which allows listing word matches as you type.
This commit is contained in:
parent
890021379c
commit
40fa922de6
|
@ -721,15 +721,22 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref,
|
||||||
if ( i->id == id )
|
if ( i->id == id )
|
||||||
{
|
{
|
||||||
// Found the corresponding program.
|
// Found the corresponding program.
|
||||||
Programs::ArticleRequest * req = new Programs::ArticleRequest(
|
Programs::RunInstance * req = new Programs::RunInstance;
|
||||||
url.path().mid( 1 ), *i );
|
|
||||||
|
|
||||||
connect( req, SIGNAL( finished() ), req, SLOT( deleteLater() ) );
|
connect( req, SIGNAL(finished(QByteArray,QString)),
|
||||||
|
req, SLOT( deleteLater() ) );
|
||||||
|
|
||||||
// Delete the request if it has finished already
|
QString error;
|
||||||
if ( req->isFinished() )
|
|
||||||
|
// Delete the request if it fails to start
|
||||||
|
if ( !req->start( *i, url.path().mid( 1 ), error ) )
|
||||||
|
{
|
||||||
delete req;
|
delete req;
|
||||||
|
|
||||||
|
QMessageBox::critical( this, tr( "GoldenDict" ),
|
||||||
|
error );
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,6 +312,7 @@ struct Program
|
||||||
Audio,
|
Audio,
|
||||||
PlainText,
|
PlainText,
|
||||||
Html,
|
Html,
|
||||||
|
PrefixMatch,
|
||||||
MaxTypeValue
|
MaxTypeValue
|
||||||
} type;
|
} type;
|
||||||
QString id, name, commandLine;
|
QString id, name, commandLine;
|
||||||
|
|
210
programs.cc
210
programs.cc
|
@ -40,8 +40,23 @@ public:
|
||||||
|
|
||||||
virtual QIcon getIcon() throw();
|
virtual QIcon getIcon() throw();
|
||||||
|
|
||||||
virtual sptr< WordSearchRequest > prefixMatch( wstring const & /*word*/,
|
virtual sptr< WordSearchRequest > prefixMatch( wstring const & word,
|
||||||
unsigned long /*maxResults*/ ) throw( std::exception )
|
unsigned long maxResults )
|
||||||
|
throw( std::exception );
|
||||||
|
|
||||||
|
virtual sptr< DataRequest > getArticle( wstring const &,
|
||||||
|
vector< wstring > const & alts,
|
||||||
|
wstring const & )
|
||||||
|
throw( std::exception );
|
||||||
|
};
|
||||||
|
|
||||||
|
sptr< WordSearchRequest > ProgramsDictionary::prefixMatch( wstring const & word,
|
||||||
|
unsigned long /*maxResults*/ )
|
||||||
|
throw( std::exception )
|
||||||
|
{
|
||||||
|
if ( prg.type == Config::Program::PrefixMatch )
|
||||||
|
return new ProgramWordSearchRequest( gd::toQString( word ), prg );
|
||||||
|
else
|
||||||
{
|
{
|
||||||
sptr< WordSearchRequestInstant > sr = new WordSearchRequestInstant;
|
sptr< WordSearchRequestInstant > sr = new WordSearchRequestInstant;
|
||||||
|
|
||||||
|
@ -49,50 +64,52 @@ public:
|
||||||
|
|
||||||
return sr;
|
return sr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual sptr< DataRequest > getArticle( wstring const &, vector< wstring > const & alts,
|
sptr< Dictionary::DataRequest > ProgramsDictionary::getArticle(
|
||||||
wstring const & )
|
wstring const & word, vector< wstring > const &, wstring const & )
|
||||||
throw( std::exception );
|
|
||||||
};
|
|
||||||
|
|
||||||
sptr< DataRequest > ProgramsDictionary::getArticle( wstring const & word,
|
|
||||||
vector< wstring > const &,
|
|
||||||
wstring const & )
|
|
||||||
throw( std::exception )
|
throw( std::exception )
|
||||||
{
|
{
|
||||||
if ( prg.type == Config::Program::Audio )
|
switch( prg.type )
|
||||||
{
|
{
|
||||||
// Audio results are instantaneous
|
case Config::Program::Audio:
|
||||||
string result;
|
{
|
||||||
|
// Audio results are instantaneous
|
||||||
|
string result;
|
||||||
|
|
||||||
string wordUtf8( Utf8::encode( word ) );
|
string wordUtf8( Utf8::encode( word ) );
|
||||||
|
|
||||||
result += "<table class=\"programs_play\"><tr>";
|
result += "<table class=\"programs_play\"><tr>";
|
||||||
|
|
||||||
QUrl url;
|
QUrl url;
|
||||||
url.setScheme( "gdprg" );
|
url.setScheme( "gdprg" );
|
||||||
url.setHost( QString::fromUtf8( getId().c_str() ) );
|
url.setHost( QString::fromUtf8( getId().c_str() ) );
|
||||||
url.setPath( QString::fromUtf8( wordUtf8.c_str() ) );
|
url.setPath( QString::fromUtf8( wordUtf8.c_str() ) );
|
||||||
|
|
||||||
string ref = string( "\"" ) + url.toEncoded().data() + "\"";
|
string ref = string( "\"" ) + url.toEncoded().data() + "\"";
|
||||||
|
|
||||||
result += addAudioLink( ref, getId() );
|
result += addAudioLink( ref, getId() );
|
||||||
|
|
||||||
result += "<td><a href=" + ref + "><img src=\"qrcx://localhost/icons/playsound.png\" border=\"0\" alt=\"Play\"/></a></td>";
|
result += "<td><a href=" + ref + "><img src=\"qrcx://localhost/icons/playsound.png\" border=\"0\" alt=\"Play\"/></a></td>";
|
||||||
result += "<td><a href=" + ref + ">" +
|
result += "<td><a href=" + ref + ">" +
|
||||||
Html::escape( wordUtf8 ) + "</a></td>";
|
Html::escape( wordUtf8 ) + "</a></td>";
|
||||||
result += "</tr></table>";
|
result += "</tr></table>";
|
||||||
|
|
||||||
sptr< Dictionary::DataRequestInstant > ret =
|
sptr< DataRequestInstant > ret = new DataRequestInstant( true );
|
||||||
new Dictionary::DataRequestInstant( true );
|
|
||||||
|
|
||||||
ret->getData().resize( result.size() );
|
ret->getData().resize( result.size() );
|
||||||
|
|
||||||
memcpy( &(ret->getData().front()), result.data(), result.size() );
|
memcpy( &(ret->getData().front()), result.data(), result.size() );
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Config::Program::Html:
|
||||||
|
case Config::Program::PlainText:
|
||||||
|
return new ProgramDataRequest( gd::toQString( word ), prg );
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new DataRequestInstant( false );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return new ArticleRequest( gd::toQString( word ), prg );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon ProgramsDictionary::getIcon() throw()
|
QIcon ProgramsDictionary::getIcon() throw()
|
||||||
|
@ -102,9 +119,17 @@ QIcon ProgramsDictionary::getIcon() throw()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArticleRequest::ArticleRequest( QString const & word,
|
RunInstance::RunInstance(): process( this )
|
||||||
Config::Program const & prg_ ):
|
{
|
||||||
prg( prg_ ), process( this )
|
connect( this, SIGNAL(processFinished()), this,
|
||||||
|
SLOT(handleProcessFinished()), Qt::QueuedConnection );
|
||||||
|
connect( &process, SIGNAL(finished(int)), this, SIGNAL(processFinished()));
|
||||||
|
connect( &process, SIGNAL(error(QProcess::ProcessError)), this,
|
||||||
|
SIGNAL(processFinished()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RunInstance::start( Config::Program const & prg, QString const & word,
|
||||||
|
QString & error )
|
||||||
{
|
{
|
||||||
QStringList args = parseCommandLine( prg.commandLine );
|
QStringList args = parseCommandLine( prg.commandLine );
|
||||||
|
|
||||||
|
@ -116,34 +141,65 @@ ArticleRequest::ArticleRequest( QString const & word,
|
||||||
for( int x = 0; x < args.size(); ++x )
|
for( int x = 0; x < args.size(); ++x )
|
||||||
args[ x ].replace( "%GDWORD%", word );
|
args[ x ].replace( "%GDWORD%", word );
|
||||||
|
|
||||||
connect( this, SIGNAL(processFinished()), this,
|
|
||||||
SLOT(handleProcessFinished()), Qt::QueuedConnection );
|
|
||||||
connect( &process, SIGNAL(finished(int)), this, SIGNAL(processFinished()));
|
|
||||||
connect( &process, SIGNAL(error(QProcess::ProcessError)), this,
|
|
||||||
SIGNAL(processFinished()) );
|
|
||||||
|
|
||||||
process.start( programName, args );
|
process.start( programName, args );
|
||||||
process.write( word.toLocal8Bit() );
|
process.write( word.toLocal8Bit() );
|
||||||
process.closeWriteChannel();
|
process.closeWriteChannel();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setErrorString( tr( "No program name was given." ) );
|
error = tr( "No program name was given." );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunInstance::handleProcessFinished()
|
||||||
|
{
|
||||||
|
// It seems that sometimes the process isn't finished yet despite being
|
||||||
|
// signalled as such. So we wait for it here, which should hopefully be
|
||||||
|
// nearly instant.
|
||||||
|
process.waitForFinished();
|
||||||
|
|
||||||
|
QByteArray output = process.readAllStandardOutput();
|
||||||
|
|
||||||
|
QString error;
|
||||||
|
if ( process.exitStatus() != QProcess::NormalExit )
|
||||||
|
error = tr( "The program has crashed." );
|
||||||
|
else
|
||||||
|
if ( int code = process.exitCode() )
|
||||||
|
error = tr( "The program has returned exit code %1." ).arg( code );
|
||||||
|
|
||||||
|
if ( !error.isEmpty() )
|
||||||
|
{
|
||||||
|
QByteArray err = process.readAllStandardError();
|
||||||
|
|
||||||
|
if ( !err.isEmpty() )
|
||||||
|
error += "\n\n" + QString::fromLocal8Bit( err );
|
||||||
|
}
|
||||||
|
|
||||||
|
emit finished( output, error );
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramDataRequest::ProgramDataRequest( QString const & word,
|
||||||
|
Config::Program const & prg_ ):
|
||||||
|
prg( prg_ )
|
||||||
|
{
|
||||||
|
connect( &instance, SIGNAL(finished(QByteArray,QString)),
|
||||||
|
this, SLOT(instanceFinished(QByteArray,QString)) );
|
||||||
|
|
||||||
|
QString error;
|
||||||
|
if ( !instance.start( prg, word, error ) )
|
||||||
|
{
|
||||||
|
setErrorString( error );
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArticleRequest::handleProcessFinished()
|
void ProgramDataRequest::instanceFinished( QByteArray output, QString error )
|
||||||
{
|
{
|
||||||
if ( !isFinished() )
|
if ( !isFinished() )
|
||||||
{
|
{
|
||||||
// It seems that sometimes the process isn't finished yet despite being
|
|
||||||
// signalled as such. So we wait for it here, which should hopefully be
|
|
||||||
// nearly instant.
|
|
||||||
process.waitForFinished();
|
|
||||||
|
|
||||||
QByteArray output = process.readAllStandardOutput();
|
|
||||||
|
|
||||||
if ( !output.isEmpty() )
|
if ( !output.isEmpty() )
|
||||||
{
|
{
|
||||||
string result = "<div class='programs_";
|
string result = "<div class='programs_";
|
||||||
|
@ -167,32 +223,56 @@ void ArticleRequest::handleProcessFinished()
|
||||||
hasAnyData = true;
|
hasAnyData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString error;
|
|
||||||
if ( process.exitStatus() != QProcess::NormalExit )
|
|
||||||
error = tr( "The program has crashed." );
|
|
||||||
else
|
|
||||||
if ( int code = process.exitCode() )
|
|
||||||
error = tr( "The program has returned exit code %1." ).arg( code );
|
|
||||||
|
|
||||||
if ( !error.isEmpty() )
|
if ( !error.isEmpty() )
|
||||||
{
|
|
||||||
QByteArray err = process.readAllStandardError();
|
|
||||||
|
|
||||||
if ( !err.isEmpty() )
|
|
||||||
error += "\n\n" + QString::fromLocal8Bit( err );
|
|
||||||
|
|
||||||
setErrorString( error );
|
setErrorString( error );
|
||||||
}
|
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArticleRequest::cancel()
|
void ProgramDataRequest::cancel()
|
||||||
{
|
{
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgramWordSearchRequest::ProgramWordSearchRequest( QString const & word,
|
||||||
|
Config::Program const & prg_ ):
|
||||||
|
prg( prg_ )
|
||||||
|
{
|
||||||
|
connect( &instance, SIGNAL(finished(QByteArray,QString)),
|
||||||
|
this, SLOT(instanceFinished(QByteArray,QString)) );
|
||||||
|
|
||||||
|
QString error;
|
||||||
|
if ( !instance.start( prg, word, error ) )
|
||||||
|
{
|
||||||
|
setErrorString( error );
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramWordSearchRequest::instanceFinished( QByteArray output, QString error )
|
||||||
|
{
|
||||||
|
if ( !isFinished() )
|
||||||
|
{
|
||||||
|
// Handle any Windows artifacts
|
||||||
|
output.replace( "\r\n", "\n" );
|
||||||
|
QStringList result =
|
||||||
|
QString::fromUtf8( output ).split( "\n", QString::SkipEmptyParts );
|
||||||
|
|
||||||
|
for( int x = 0; x < result.size(); ++x )
|
||||||
|
matches.push_back( Dictionary::WordMatch( gd::toWString( result[ x ] ) ) );
|
||||||
|
|
||||||
|
if ( !error.isEmpty() )
|
||||||
|
setErrorString( error );
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramWordSearchRequest::cancel()
|
||||||
|
{
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||||
Config::Programs const & programs )
|
Config::Programs const & programs )
|
||||||
|
|
49
programs.hh
49
programs.hh
|
@ -19,18 +19,25 @@ using gd::wstring;
|
||||||
vector< sptr< Dictionary::Class > > makeDictionaries( Config::Programs const & )
|
vector< sptr< Dictionary::Class > > makeDictionaries( Config::Programs const & )
|
||||||
throw( std::exception );
|
throw( std::exception );
|
||||||
|
|
||||||
class ArticleRequest: public Dictionary::DataRequest
|
class RunInstance: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Config::Program prg;
|
|
||||||
QProcess process;
|
QProcess process;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ArticleRequest( QString const & word, Config::Program const & );
|
RunInstance();
|
||||||
|
|
||||||
virtual void cancel();
|
// Starts the process. Should only be used once. The finished() signal will
|
||||||
|
// be emitted once it finishes. If there's an error, returns false and the
|
||||||
|
// description is saved to 'error'.
|
||||||
|
bool start( Config::Program const &, QString const & word, QString & error );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Connect to this signal to get run results
|
||||||
|
void finished( QByteArray stdout, QString error );
|
||||||
|
|
||||||
|
// Used internally only
|
||||||
signals:
|
signals:
|
||||||
void processFinished();
|
void processFinished();
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -38,6 +45,40 @@ private slots:
|
||||||
void handleProcessFinished();
|
void handleProcessFinished();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ProgramDataRequest: public Dictionary::DataRequest
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Config::Program prg;
|
||||||
|
RunInstance instance;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ProgramDataRequest( QString const & word, Config::Program const & );
|
||||||
|
|
||||||
|
virtual void cancel();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void instanceFinished( QByteArray output, QString error );
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProgramWordSearchRequest: public Dictionary::WordSearchRequest
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Config::Program prg;
|
||||||
|
RunInstance instance;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ProgramWordSearchRequest( QString const & word, Config::Program const & );
|
||||||
|
|
||||||
|
virtual void cancel();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void instanceFinished( QByteArray output, QString error );
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -52,7 +52,7 @@ Sources::Sources( QWidget * parent, Config::Paths const & paths,
|
||||||
// Make sure this thing will be large enough
|
// Make sure this thing will be large enough
|
||||||
ui.programs->setColumnWidth( 1,
|
ui.programs->setColumnWidth( 1,
|
||||||
QFontMetrics( QFont() ).width(
|
QFontMetrics( QFont() ).width(
|
||||||
ProgramTypeEditor::getNameForType( Config::Program::PlainText ) ) + 16 );
|
ProgramTypeEditor::getNameForType( Config::Program::PrefixMatch ) ) + 16 );
|
||||||
ui.programs->resizeColumnToContents( 2 );
|
ui.programs->resizeColumnToContents( 2 );
|
||||||
ui.programs->resizeColumnToContents( 3 );
|
ui.programs->resizeColumnToContents( 3 );
|
||||||
ui.programs->setItemDelegate( itemDelegate );
|
ui.programs->setItemDelegate( itemDelegate );
|
||||||
|
@ -773,6 +773,8 @@ QString ProgramTypeEditor::getNameForType( int v )
|
||||||
return tr( "Plain Text" );
|
return tr( "Plain Text" );
|
||||||
case Config::Program::Html:
|
case Config::Program::Html:
|
||||||
return tr( "Html" );
|
return tr( "Html" );
|
||||||
|
case Config::Program::PrefixMatch:
|
||||||
|
return tr( "Prefix Match" );
|
||||||
default:
|
default:
|
||||||
return tr( "Unknown" );
|
return tr( "Unknown" );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue