From 40fa922de6284239f68d54ed6f0ebb81d0030371 Mon Sep 17 00:00:00 2001 From: Konstantin Isakov Date: Sat, 4 Jun 2011 14:35:09 -0700 Subject: [PATCH] Add new program type - 'Prefix Match', which allows listing word matches as you type. --- articleview.cc | 17 ++-- config.hh | 1 + programs.cc | 210 ++++++++++++++++++++++++++++++++++--------------- programs.hh | 49 +++++++++++- sources.cc | 4 +- 5 files changed, 206 insertions(+), 75 deletions(-) diff --git a/articleview.cc b/articleview.cc index 95399a44..6bf106e0 100644 --- a/articleview.cc +++ b/articleview.cc @@ -721,15 +721,22 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, if ( i->id == id ) { // Found the corresponding program. - Programs::ArticleRequest * req = new Programs::ArticleRequest( - url.path().mid( 1 ), *i ); + Programs::RunInstance * req = new Programs::RunInstance; - connect( req, SIGNAL( finished() ), req, SLOT( deleteLater() ) ); + connect( req, SIGNAL(finished(QByteArray,QString)), + req, SLOT( deleteLater() ) ); - // Delete the request if it has finished already - if ( req->isFinished() ) + QString error; + + // Delete the request if it fails to start + if ( !req->start( *i, url.path().mid( 1 ), error ) ) + { delete req; + QMessageBox::critical( this, tr( "GoldenDict" ), + error ); + } + return; } } diff --git a/config.hh b/config.hh index 0ddaead6..9480ef26 100644 --- a/config.hh +++ b/config.hh @@ -312,6 +312,7 @@ struct Program Audio, PlainText, Html, + PrefixMatch, MaxTypeValue } type; QString id, name, commandLine; diff --git a/programs.cc b/programs.cc index 97762405..408eacdf 100644 --- a/programs.cc +++ b/programs.cc @@ -40,8 +40,23 @@ public: virtual QIcon getIcon() throw(); - virtual sptr< WordSearchRequest > prefixMatch( wstring const & /*word*/, - unsigned long /*maxResults*/ ) throw( std::exception ) + virtual sptr< WordSearchRequest > prefixMatch( wstring const & word, + 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; @@ -49,50 +64,52 @@ public: return sr; } +} - virtual sptr< DataRequest > getArticle( wstring const &, vector< wstring > const & alts, - wstring const & ) - throw( std::exception ); -}; - -sptr< DataRequest > ProgramsDictionary::getArticle( wstring const & word, - vector< wstring > const &, - wstring const & ) +sptr< Dictionary::DataRequest > ProgramsDictionary::getArticle( + wstring const & word, vector< wstring > const &, wstring const & ) throw( std::exception ) { - if ( prg.type == Config::Program::Audio ) + switch( prg.type ) { - // Audio results are instantaneous - string result; + case Config::Program::Audio: + { + // Audio results are instantaneous + string result; - string wordUtf8( Utf8::encode( word ) ); + string wordUtf8( Utf8::encode( word ) ); - result += ""; + result += "
"; - QUrl url; - url.setScheme( "gdprg" ); - url.setHost( QString::fromUtf8( getId().c_str() ) ); - url.setPath( QString::fromUtf8( wordUtf8.c_str() ) ); + QUrl url; + url.setScheme( "gdprg" ); + url.setHost( QString::fromUtf8( getId().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 += ""; - result += ""; - result += "
\"Play\"/" + - Html::escape( wordUtf8 ) + "
"; + result += "\"Play\"/"; + result += "" + + Html::escape( wordUtf8 ) + ""; + result += ""; - sptr< Dictionary::DataRequestInstant > ret = - new Dictionary::DataRequestInstant( true ); + sptr< DataRequestInstant > ret = new DataRequestInstant( true ); - ret->getData().resize( result.size() ); + ret->getData().resize( result.size() ); - memcpy( &(ret->getData().front()), result.data(), result.size() ); - return ret; + memcpy( &(ret->getData().front()), result.data(), result.size() ); + 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() @@ -102,9 +119,17 @@ QIcon ProgramsDictionary::getIcon() throw() } -ArticleRequest::ArticleRequest( QString const & word, - Config::Program const & prg_ ): - prg( prg_ ), process( this ) +RunInstance::RunInstance(): 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 ); @@ -116,34 +141,65 @@ ArticleRequest::ArticleRequest( QString const & word, for( int x = 0; x < args.size(); ++x ) 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.write( word.toLocal8Bit() ); process.closeWriteChannel(); + + return true; } 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(); } } -void ArticleRequest::handleProcessFinished() +void ProgramDataRequest::instanceFinished( QByteArray output, QString error ) { 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() ) { string result = "
setColumnWidth( 1, QFontMetrics( QFont() ).width( - ProgramTypeEditor::getNameForType( Config::Program::PlainText ) ) + 16 ); + ProgramTypeEditor::getNameForType( Config::Program::PrefixMatch ) ) + 16 ); ui.programs->resizeColumnToContents( 2 ); ui.programs->resizeColumnToContents( 3 ); ui.programs->setItemDelegate( itemDelegate ); @@ -773,6 +773,8 @@ QString ProgramTypeEditor::getNameForType( int v ) return tr( "Plain Text" ); case Config::Program::Html: return tr( "Html" ); + case Config::Program::PrefixMatch: + return tr( "Prefix Match" ); default: return tr( "Unknown" ); }