mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-27 15:24:05 +00:00
Add support for launching arbitrary executables (tts, manpages etc).
This commit is contained in:
parent
b173898c5d
commit
9960efc00d
|
@ -378,6 +378,24 @@ div.sdct_x
|
|||
color: red;
|
||||
}
|
||||
|
||||
/************* Programs **************/
|
||||
|
||||
/* A table which contains a play icon and a word's link */
|
||||
.programs_play
|
||||
{
|
||||
margin-top: 8px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.programs_play a
|
||||
{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.programs_plaintext, .programs_html
|
||||
{
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/************* MediaWiki articles *****************
|
||||
The following consist of excerpts from different .css files edited
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "folding.hh"
|
||||
#include "wstring_qt.hh"
|
||||
#include "webmultimediadownload.hh"
|
||||
#include "programs.hh"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
|
@ -709,6 +710,35 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref,
|
|||
resourceDownloadFinished(); // Check any requests finished already
|
||||
}
|
||||
else
|
||||
if ( url.scheme() == "gdprg" )
|
||||
{
|
||||
// Program. Run it.
|
||||
QString id( url.host() );
|
||||
|
||||
for( Config::Programs::const_iterator i = cfg.programs.begin();
|
||||
i != cfg.programs.end(); ++i )
|
||||
{
|
||||
if ( i->id == id )
|
||||
{
|
||||
// Found the corresponding program.
|
||||
Programs::ArticleRequest * req = new Programs::ArticleRequest(
|
||||
url.path().mid( 1 ), *i );
|
||||
|
||||
connect( req, SIGNAL( finished() ), req, SLOT( deleteLater() ) );
|
||||
|
||||
// Delete the request if it has finished already
|
||||
if ( req->isFinished() )
|
||||
delete req;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Still here? No such program exists.
|
||||
QMessageBox::critical( this, tr( "GoldenDict" ),
|
||||
tr( "The referenced audio program doesn't exist." ) );
|
||||
}
|
||||
else
|
||||
if ( isExternalLink( url ) )
|
||||
{
|
||||
// Use the system handler for the conventional external links
|
||||
|
@ -1037,17 +1067,8 @@ void ArticleView::resourceDownloadFinished()
|
|||
{
|
||||
ExternalViewer * viewer = new ExternalViewer( this, data, "wav", cfg.preferences.audioPlaybackProgram.trimmed() );
|
||||
|
||||
try
|
||||
{
|
||||
viewer->start();
|
||||
|
||||
// Once started, it will erase itself
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
delete viewer;
|
||||
throw;
|
||||
}
|
||||
// Once started, it will erase itself
|
||||
viewer->start();
|
||||
}
|
||||
catch( ExternalViewer::Ex & e )
|
||||
{
|
||||
|
@ -1091,7 +1112,7 @@ void ArticleView::resourceDownloadFinished()
|
|||
}
|
||||
else
|
||||
{
|
||||
// This one had no data. Erase it.
|
||||
// This one had no data. Erase it.
|
||||
resourceDownloadRequests.erase( i++ );
|
||||
}
|
||||
}
|
||||
|
|
69
config.cc
69
config.cc
|
@ -162,6 +162,20 @@ WebSites makeDefaultWebSites()
|
|||
return ws;
|
||||
}
|
||||
|
||||
Programs makeDefaultPrograms()
|
||||
{
|
||||
Programs programs;
|
||||
|
||||
// The following list doesn't make a lot of sense under Windows
|
||||
#ifndef Q_WS_WIN
|
||||
programs.push_back( Program( false, Program::Audio, "428b4c2b905ef568a43d9a16f59559b0", "Festival", "festival --tts" ) );
|
||||
programs.push_back( Program( false, Program::Audio, "2cf8b3a60f27e1ac812de0b57c148340", "Espeak", "espeak %GDWORD%" ) );
|
||||
programs.push_back( Program( false, Program::PlainText, "4f898f7582596cea518c6b0bfdceb8b3", "Manpages", "man -a %GDWORD%" ) );
|
||||
#endif
|
||||
|
||||
return programs;
|
||||
}
|
||||
|
||||
/// Sets option to true of false if node is "1" or "0" respectively, or leaves
|
||||
/// it intact if it's neither "1" nor "0".
|
||||
void applyBoolOption( bool & option, QDomNode const & node )
|
||||
|
@ -459,6 +473,30 @@ Class load() throw( exError )
|
|||
else
|
||||
c.forvo.languageCodes = "en, ru"; // Default demo values
|
||||
|
||||
QDomNode programs = root.namedItem( "programs" );
|
||||
|
||||
if ( !programs.isNull() )
|
||||
{
|
||||
QDomNodeList nl = programs.toElement().elementsByTagName( "program" );
|
||||
|
||||
for( unsigned x = 0; x < nl.length(); ++x )
|
||||
{
|
||||
QDomElement pr = nl.item( x ).toElement();
|
||||
|
||||
Program p;
|
||||
|
||||
p.id = pr.attribute( "id" );
|
||||
p.name = pr.attribute( "name" );
|
||||
p.commandLine = pr.attribute( "commandLine" );
|
||||
p.enabled = ( pr.attribute( "enabled" ) == "1" );
|
||||
p.type = (Program::Type)( pr.attribute( "type" ).toInt() );
|
||||
|
||||
c.programs.push_back( p );
|
||||
}
|
||||
}
|
||||
else
|
||||
c.programs = makeDefaultPrograms();
|
||||
|
||||
QDomNode mws = root.namedItem( "mediawikis" );
|
||||
|
||||
if ( !mws.isNull() )
|
||||
|
@ -916,6 +954,37 @@ void save( Class const & c ) throw( exError )
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
QDomElement programs = dd.createElement( "programs" );
|
||||
root.appendChild( programs );
|
||||
|
||||
for( Programs::const_iterator i = c.programs.begin(); i != c.programs.end(); ++i )
|
||||
{
|
||||
QDomElement p = dd.createElement( "program" );
|
||||
programs.appendChild( p );
|
||||
|
||||
QDomAttr id = dd.createAttribute( "id" );
|
||||
id.setValue( i->id );
|
||||
p.setAttributeNode( id );
|
||||
|
||||
QDomAttr name = dd.createAttribute( "name" );
|
||||
name.setValue( i->name );
|
||||
p.setAttributeNode( name );
|
||||
|
||||
QDomAttr commandLine = dd.createAttribute( "commandLine" );
|
||||
commandLine.setValue( i->commandLine );
|
||||
p.setAttributeNode( commandLine );
|
||||
|
||||
QDomAttr enabled = dd.createAttribute( "enabled" );
|
||||
enabled.setValue( i->enabled ? "1" : "0" );
|
||||
p.setAttributeNode( enabled );
|
||||
|
||||
QDomAttr type = dd.createAttribute( "type" );
|
||||
type.setValue( QString::number( i->type ) );
|
||||
p.setAttributeNode( type );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
QDomElement muted = dd.createElement( "mutedDictionaries" );
|
||||
|
|
34
config.hh
34
config.hh
|
@ -304,6 +304,39 @@ struct Forvo
|
|||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
struct Program
|
||||
{
|
||||
bool enabled;
|
||||
enum Type
|
||||
{
|
||||
Audio,
|
||||
PlainText,
|
||||
Html,
|
||||
MaxTypeValue
|
||||
} type;
|
||||
QString id, name, commandLine;
|
||||
|
||||
Program(): enabled( false )
|
||||
{}
|
||||
|
||||
Program( bool enabled_, Type type_, QString const & id_,
|
||||
QString const & name_, QString const & commandLine_ ):
|
||||
enabled( enabled_ ), type( type_ ), id( id_ ), name( name_ ),
|
||||
commandLine( commandLine_ ) {}
|
||||
|
||||
bool operator == ( Program const & other ) const
|
||||
{ return enabled == other.enabled &&
|
||||
type == other.type &&
|
||||
name == other.name &&
|
||||
commandLine == other.commandLine;
|
||||
}
|
||||
|
||||
bool operator != ( Program const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
typedef vector< Program > Programs;
|
||||
|
||||
/// Dictionaries which are temporarily disabled via the dictionary bar.
|
||||
typedef QSet< QString > MutedDictionaries;
|
||||
|
||||
|
@ -320,6 +353,7 @@ struct Class
|
|||
Hunspell hunspell;
|
||||
Transliteration transliteration;
|
||||
Forvo forvo;
|
||||
Programs programs;
|
||||
|
||||
unsigned lastMainGroupId; // Last used group in main window
|
||||
unsigned lastPopupGroupId; // Last used group in popup window
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "config.hh"
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
|
||||
namespace Dictionary {
|
||||
|
||||
|
@ -218,4 +220,13 @@ bool needToRebuildIndex( vector< string > const & dictionaryFiles,
|
|||
return fileInfo.lastModified().toTime_t() < lastModified;
|
||||
}
|
||||
|
||||
QString generateRandomDictionaryId()
|
||||
{
|
||||
return QString(
|
||||
QCryptographicHash::hash(
|
||||
QDateTime::currentDateTime().toString( "\"Random\"dd.MM.yyyy hh:mm:ss.zzz" ).toUtf8(),
|
||||
QCryptographicHash::Md5 ).toHex() );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -403,6 +403,10 @@ string makeDictionaryId( vector< string > const & dictionaryFiles ) throw();
|
|||
bool needToRebuildIndex( vector< string > const & dictionaryFiles,
|
||||
string const & indexFile ) throw();
|
||||
|
||||
/// Returns a random dictionary id useful for interactively created
|
||||
/// dictionaries.
|
||||
QString generateRandomDictionaryId();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,7 +16,7 @@ EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
|
|||
dictNetMgr( dictNetMgr_ ),
|
||||
origCfg( cfg ),
|
||||
sources( this, cfg.paths, cfg.soundDirs, cfg.hunspell, cfg.transliteration,
|
||||
cfg.forvo, cfg.mediawikis, cfg.webSites ),
|
||||
cfg.forvo, cfg.mediawikis, cfg.webSites, cfg.programs ),
|
||||
orderAndProps( new OrderAndProps( this, cfg.dictionaryOrder, cfg.inactiveDictionaries,
|
||||
dictionaries ) ),
|
||||
groups( new Groups( this, dictionaries, cfg.groups, orderAndProps->getCurrentDictionaryOrder() ) ),
|
||||
|
@ -140,7 +140,8 @@ bool EditDictionaries::isSourcesChanged() const
|
|||
sources.getTransliteration() != cfg.transliteration ||
|
||||
sources.getForvo() != cfg.forvo ||
|
||||
sources.getMediaWikis() != cfg.mediawikis ||
|
||||
sources.getWebSites() != cfg.webSites;
|
||||
sources.getWebSites() != cfg.webSites ||
|
||||
sources.getPrograms() != cfg.programs;
|
||||
}
|
||||
|
||||
void EditDictionaries::acceptChangedSources( bool rebuildGroups )
|
||||
|
@ -158,6 +159,7 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
|
|||
cfg.forvo = sources.getForvo();
|
||||
cfg.mediawikis = sources.getMediaWikis();
|
||||
cfg.webSites = sources.getWebSites();
|
||||
cfg.programs = sources.getPrograms();
|
||||
|
||||
groupInstances.clear(); // Those hold pointers to dictionaries, we need to
|
||||
// free them.
|
||||
|
|
|
@ -44,7 +44,7 @@ private slots:
|
|||
void on_tabs_currentChanged( int index );
|
||||
|
||||
void rescanSources();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool isSourcesChanged() const;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* This file is (c) 2008-2011 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "externalviewer.hh"
|
||||
#include <QDir>
|
||||
#include "externalviewer.hh"
|
||||
|
||||
using std::vector;
|
||||
|
||||
|
@ -23,47 +23,17 @@ ExternalViewer::ExternalViewer( QObject * parent, vector< char > const & data,
|
|||
tempFile.close();
|
||||
|
||||
printf( "%s\n", tempFile.fileName().toLocal8Bit().data() );
|
||||
|
||||
connect( &viewer, SIGNAL( finished( int, QProcess::ExitStatus ) ),
|
||||
this, SLOT( viewerFinished( int, QProcess::ExitStatus ) ) );
|
||||
|
||||
connect( this, SIGNAL( finished( ExternalViewer * ) ),
|
||||
&ExternalViewerDeleter::instance(), SLOT( deleteExternalViewer( ExternalViewer * ) ),
|
||||
Qt::QueuedConnection );
|
||||
}
|
||||
|
||||
ExternalViewer::~ExternalViewer()
|
||||
{
|
||||
// No need to delete us once we're being destructed. This fixes some
|
||||
// double-free corruption if the object is being freed prematurely.
|
||||
disconnect( this, SIGNAL( finished( ExternalViewer * ) ),
|
||||
&ExternalViewerDeleter::instance(), SLOT( deleteExternalViewer( ExternalViewer * ) ) );
|
||||
}
|
||||
|
||||
void ExternalViewer::start() throw( exCantRunViewer )
|
||||
{
|
||||
connect( &viewer, SIGNAL( finished( int, QProcess::ExitStatus ) ),
|
||||
this, SLOT( deleteLater() ) );
|
||||
connect( &viewer, SIGNAL( error( QProcess::ProcessError ) ),
|
||||
this, SLOT( deleteLater() ) );
|
||||
|
||||
viewer.start( viewerProgram, QStringList( tempFileName ), QIODevice::NotOpen );
|
||||
|
||||
if ( !viewer.waitForStarted() )
|
||||
throw exCantRunViewer( viewerProgram.toStdString() );
|
||||
}
|
||||
|
||||
void ExternalViewer::viewerFinished( int, QProcess::ExitStatus )
|
||||
{
|
||||
emit finished( this );
|
||||
}
|
||||
|
||||
ExternalViewerDeleter & ExternalViewerDeleter::instance()
|
||||
{
|
||||
static ExternalViewerDeleter evd( 0 );
|
||||
|
||||
return evd;
|
||||
}
|
||||
|
||||
void ExternalViewerDeleter::deleteExternalViewer( ExternalViewer * e )
|
||||
{
|
||||
printf( "Deleting external viewer\n" );
|
||||
|
||||
delete e;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,35 +30,9 @@ public:
|
|||
QString const & extension, QString const & viewerProgram )
|
||||
throw( exCantCreateTempFile );
|
||||
|
||||
~ExternalViewer();
|
||||
|
||||
// Once this is called, the object will be deleted when it's done, even if
|
||||
// the function throws.
|
||||
void start() throw( exCantRunViewer );
|
||||
|
||||
private slots:
|
||||
|
||||
void viewerFinished( int, QProcess::ExitStatus );
|
||||
|
||||
signals:
|
||||
|
||||
void finished( ExternalViewer * );
|
||||
};
|
||||
|
||||
class ExternalViewerDeleter: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static ExternalViewerDeleter & instance();
|
||||
|
||||
public slots:
|
||||
|
||||
void deleteExternalViewer( ExternalViewer * e );
|
||||
|
||||
private:
|
||||
|
||||
ExternalViewerDeleter( QObject * parent ): QObject( parent )
|
||||
{}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -156,7 +156,8 @@ HEADERS += folding.hh \
|
|||
webmultimediadownload.hh \
|
||||
forvo.hh \
|
||||
country.hh \
|
||||
about.hh
|
||||
about.hh \
|
||||
programs.hh
|
||||
FORMS += groups.ui \
|
||||
dictgroupwidget.ui \
|
||||
mainwindow.ui \
|
||||
|
@ -238,7 +239,8 @@ SOURCES += folding.cc \
|
|||
webmultimediadownload.cc \
|
||||
forvo.cc \
|
||||
country.cc \
|
||||
about.cc
|
||||
about.cc \
|
||||
programs.cc
|
||||
win32 {
|
||||
SOURCES += mouseover_win32/ThTypes.c
|
||||
HEADERS += mouseover_win32/ThTypes.h
|
||||
|
|
BIN
icons/programs.png
Normal file
BIN
icons/programs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
|
@ -17,6 +17,7 @@
|
|||
#include "greektranslit.hh"
|
||||
#include "website.hh"
|
||||
#include "forvo.hh"
|
||||
#include "programs.hh"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
@ -230,6 +231,14 @@ void loadDictionaries( QWidget * parent, bool showInitially,
|
|||
dictionaries.insert( dictionaries.end(), dicts.begin(), dicts.end() );
|
||||
}
|
||||
|
||||
//// Programs
|
||||
{
|
||||
vector< sptr< Dictionary::Class > > dicts =
|
||||
Programs::makeDictionaries( cfg.programs );
|
||||
|
||||
dictionaries.insert( dictionaries.end(), dicts.begin(), dicts.end() );
|
||||
}
|
||||
|
||||
printf( "Load done\n" );
|
||||
|
||||
// Remove any stale index files
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<file>icons/reload.png</file>
|
||||
<file>icons/programicon.png</file>
|
||||
<file>icons/programicon_scan.png</file>
|
||||
<file>icons/programs.png</file>
|
||||
<file>icons/wizard.png</file>
|
||||
<file>icons/warning.png</file>
|
||||
<file>article-style.css</file>
|
||||
|
|
249
sources.cc
249
sources.cc
|
@ -4,8 +4,6 @@
|
|||
#include "sources.hh"
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
Sources::Sources( QWidget * parent, Config::Paths const & paths,
|
||||
|
@ -14,15 +12,28 @@ Sources::Sources( QWidget * parent, Config::Paths const & paths,
|
|||
Config::Transliteration const & trs,
|
||||
Config::Forvo const & forvo,
|
||||
Config::MediaWikis const & mediawikis,
|
||||
Config::WebSites const & webSites ): QWidget( parent ),
|
||||
Config::WebSites const & webSites,
|
||||
Config::Programs const & programs ): QWidget( parent ),
|
||||
itemDelegate( new QItemDelegate( this ) ),
|
||||
itemEditorFactory( new QItemEditorFactory() ),
|
||||
mediawikisModel( this, mediawikis ),
|
||||
webSitesModel( this, webSites ),
|
||||
programsModel( this, programs ),
|
||||
pathsModel( this, paths ),
|
||||
soundDirsModel( this, soundDirs ),
|
||||
hunspellDictsModel( this, hunspell )
|
||||
{
|
||||
ui.setupUi( this );
|
||||
|
||||
// TODO: will programTypeEditorCreator and itemEditorFactory be destoryed by
|
||||
// anyone?
|
||||
QItemEditorCreatorBase * programTypeEditorCreator =
|
||||
new QStandardItemEditorCreator< ProgramTypeEditor >();
|
||||
|
||||
itemEditorFactory->registerEditor( QVariant::Int, programTypeEditorCreator );
|
||||
|
||||
itemDelegate->setItemEditorFactory( itemEditorFactory );
|
||||
|
||||
ui.mediaWikis->setTabKeyNavigation( true );
|
||||
ui.mediaWikis->setModel( &mediawikisModel );
|
||||
ui.mediaWikis->resizeColumnToContents( 0 );
|
||||
|
@ -35,6 +46,17 @@ Sources::Sources( QWidget * parent, Config::Paths const & paths,
|
|||
ui.webSites->resizeColumnToContents( 1 );
|
||||
ui.webSites->resizeColumnToContents( 2 );
|
||||
|
||||
ui.programs->setTabKeyNavigation( true );
|
||||
ui.programs->setModel( &programsModel );
|
||||
ui.programs->resizeColumnToContents( 0 );
|
||||
// Make sure this thing will be large enough
|
||||
ui.programs->setColumnWidth( 1,
|
||||
QFontMetrics( QFont() ).width(
|
||||
ProgramTypeEditor::getNameForType( Config::Program::PlainText ) ) + 16 );
|
||||
ui.programs->resizeColumnToContents( 2 );
|
||||
ui.programs->resizeColumnToContents( 3 );
|
||||
ui.programs->setItemDelegate( itemDelegate );
|
||||
|
||||
ui.paths->setTabKeyNavigation( true );
|
||||
ui.paths->setModel( &pathsModel );
|
||||
|
||||
|
@ -226,6 +248,30 @@ void Sources::on_removeWebSite_clicked()
|
|||
webSitesModel.removeSite( current.row() );
|
||||
}
|
||||
|
||||
void Sources::on_addProgram_clicked()
|
||||
{
|
||||
programsModel.addNewProgram();
|
||||
|
||||
QModelIndex result =
|
||||
programsModel.index( programsModel.rowCount( QModelIndex() ) - 1,
|
||||
1, QModelIndex() );
|
||||
|
||||
ui.programs->scrollTo( result );
|
||||
ui.programs->edit( result );
|
||||
}
|
||||
|
||||
void Sources::on_removeProgram_clicked()
|
||||
{
|
||||
QModelIndex current = ui.programs->currentIndex();
|
||||
|
||||
if ( current.isValid() &&
|
||||
QMessageBox::question( this, tr( "Confirm removal" ),
|
||||
tr( "Remove program <b>%1</b> from the list?" ).arg( programsModel.getCurrentPrograms()[ current.row() ].name ),
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Cancel ) == QMessageBox::Ok )
|
||||
programsModel.removeProgram( current.row() );
|
||||
}
|
||||
|
||||
Config::Hunspell Sources::getHunspell() const
|
||||
{
|
||||
Config::Hunspell h;
|
||||
|
@ -284,11 +330,7 @@ void MediaWikisModel::addNewWiki()
|
|||
|
||||
w.enabled = false;
|
||||
|
||||
// That's quite some rng
|
||||
w.id = QString(
|
||||
QCryptographicHash::hash(
|
||||
QDateTime::currentDateTime().toString( "\"MediaWiki\"dd.MM.yyyy hh:mm:ss.zzz" ).toUtf8(),
|
||||
QCryptographicHash::Md5 ).toHex() );
|
||||
w.id = Dictionary::generateRandomDictionaryId();
|
||||
|
||||
w.url = "http://";
|
||||
|
||||
|
@ -437,11 +479,7 @@ void WebSitesModel::addNewSite()
|
|||
|
||||
w.enabled = false;
|
||||
|
||||
// That's quite some rng
|
||||
w.id = QString(
|
||||
QCryptographicHash::hash(
|
||||
QDateTime::currentDateTime().toString( "\"WebSite\"dd.MM.yyyy hh:mm:ss.zzz" ).toUtf8(),
|
||||
QCryptographicHash::Md5 ).toHex() );
|
||||
w.id = Dictionary::generateRandomDictionaryId();
|
||||
|
||||
w.url = "http://";
|
||||
|
||||
|
@ -570,6 +608,191 @@ bool WebSitesModel::setData( QModelIndex const & index, const QVariant & value,
|
|||
}
|
||||
|
||||
|
||||
////////// ProgramsModel
|
||||
|
||||
ProgramsModel::ProgramsModel( QWidget * parent,
|
||||
Config::Programs const & programs_ ):
|
||||
QAbstractItemModel( parent ), programs( programs_ )
|
||||
{
|
||||
}
|
||||
|
||||
void ProgramsModel::removeProgram( int index )
|
||||
{
|
||||
beginRemoveRows( QModelIndex(), index, index );
|
||||
programs.erase( programs.begin() + index );
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void ProgramsModel::addNewProgram()
|
||||
{
|
||||
Config::Program p;
|
||||
|
||||
p.enabled = false;
|
||||
|
||||
p.id = Dictionary::generateRandomDictionaryId();
|
||||
|
||||
beginInsertRows( QModelIndex(), programs.size(), programs.size() );
|
||||
programs.push_back( p );
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
QModelIndex ProgramsModel::index( int row, int column, QModelIndex const & /*parent*/ ) const
|
||||
{
|
||||
return createIndex( row, column, 0 );
|
||||
}
|
||||
|
||||
QModelIndex ProgramsModel::parent( QModelIndex const & /*parent*/ ) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
Qt::ItemFlags ProgramsModel::flags( QModelIndex const & index ) const
|
||||
{
|
||||
Qt::ItemFlags result = QAbstractItemModel::flags( index );
|
||||
|
||||
if ( index.isValid() )
|
||||
{
|
||||
if ( !index.column() )
|
||||
result |= Qt::ItemIsUserCheckable;
|
||||
else
|
||||
result |= Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ProgramsModel::rowCount( QModelIndex const & parent ) const
|
||||
{
|
||||
if ( parent.isValid() )
|
||||
return 0;
|
||||
else
|
||||
return programs.size();
|
||||
}
|
||||
|
||||
int ProgramsModel::columnCount( QModelIndex const & parent ) const
|
||||
{
|
||||
if ( parent.isValid() )
|
||||
return 0;
|
||||
else
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariant ProgramsModel::headerData( int section, Qt::Orientation /*orientation*/, int role ) const
|
||||
{
|
||||
if ( role == Qt::DisplayRole )
|
||||
switch( section )
|
||||
{
|
||||
case 0:
|
||||
return tr( "Enabled" );
|
||||
case 1:
|
||||
return tr( "Type" );
|
||||
case 2:
|
||||
return tr( "Name" );
|
||||
case 3:
|
||||
return tr( "Command Line" );
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant ProgramsModel::data( QModelIndex const & index, int role ) const
|
||||
{
|
||||
if ( (unsigned) index.row() >= programs.size() )
|
||||
return QVariant();
|
||||
|
||||
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
||||
{
|
||||
switch( index.column() )
|
||||
{
|
||||
case 1:
|
||||
if ( role == Qt::DisplayRole )
|
||||
return ProgramTypeEditor::getNameForType( programs[ index.row() ].type );
|
||||
else
|
||||
return QVariant( ( int ) programs[ index.row() ].type );
|
||||
case 2:
|
||||
return programs[ index.row() ].name;
|
||||
case 3:
|
||||
return programs[ index.row() ].commandLine;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
if ( role == Qt::CheckStateRole && !index.column() )
|
||||
return programs[ index.row() ].enabled;
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool ProgramsModel::setData( QModelIndex const & index, const QVariant & value,
|
||||
int role )
|
||||
{
|
||||
if ( (unsigned)index.row() >= programs.size() )
|
||||
return false;
|
||||
|
||||
if ( role == Qt::CheckStateRole && !index.column() )
|
||||
{
|
||||
programs[ index.row() ].enabled = !programs[ index.row() ].enabled;
|
||||
|
||||
dataChanged( index, index );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
||||
switch( index.column() )
|
||||
{
|
||||
case 1:
|
||||
programs[ index.row() ].type = Config::Program::Type( value.toInt() );
|
||||
dataChanged( index, index );
|
||||
return true;
|
||||
case 2:
|
||||
programs[ index.row() ].name = value.toString();
|
||||
dataChanged( index, index );
|
||||
return true;
|
||||
case 3:
|
||||
programs[ index.row() ].commandLine = value.toString();
|
||||
dataChanged( index, index );
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString ProgramTypeEditor::getNameForType( int v )
|
||||
{
|
||||
switch( v )
|
||||
{
|
||||
case Config::Program::Audio:
|
||||
return tr( "Audio" );
|
||||
case Config::Program::PlainText:
|
||||
return tr( "Plain Text" );
|
||||
case Config::Program::Html:
|
||||
return tr( "Html" );
|
||||
default:
|
||||
return tr( "Unknown" );
|
||||
}
|
||||
}
|
||||
|
||||
ProgramTypeEditor::ProgramTypeEditor( QWidget * widget ): QComboBox( widget )
|
||||
{
|
||||
for( int x = 0; x < Config::Program::MaxTypeValue; ++x )
|
||||
addItem( getNameForType( x ) );
|
||||
}
|
||||
|
||||
int ProgramTypeEditor::getType() const
|
||||
{
|
||||
return currentIndex();
|
||||
}
|
||||
|
||||
void ProgramTypeEditor::setType( int t )
|
||||
{
|
||||
setCurrentIndex( t );
|
||||
}
|
||||
|
||||
////////// PathsModel
|
||||
|
||||
PathsModel::PathsModel( QWidget * parent,
|
||||
|
|
63
sources.hh
63
sources.hh
|
@ -8,6 +8,9 @@
|
|||
#include "config.hh"
|
||||
#include "hunspell.hh"
|
||||
#include <QAbstractItemModel>
|
||||
#include <QComboBox>
|
||||
#include <QItemDelegate>
|
||||
#include <QItemEditorFactory>
|
||||
|
||||
/// A model to be projected into the mediawikis view, according to Qt's MVC model
|
||||
class MediaWikisModel: public QAbstractItemModel
|
||||
|
@ -69,6 +72,52 @@ private:
|
|||
Config::WebSites webSites;
|
||||
};
|
||||
|
||||
/// A model to be projected into the programs view, according to Qt's MVC model
|
||||
class ProgramsModel: public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ProgramsModel( QWidget * parent, Config::Programs const & );
|
||||
|
||||
void removeProgram( int index );
|
||||
void addNewProgram();
|
||||
|
||||
/// Returns the sites the model currently has listed
|
||||
Config::Programs const & getCurrentPrograms() const
|
||||
{ return programs; }
|
||||
|
||||
QModelIndex index( int row, int column, QModelIndex const & parent ) const;
|
||||
QModelIndex parent( QModelIndex const & parent ) const;
|
||||
Qt::ItemFlags flags( QModelIndex const & index ) const;
|
||||
int rowCount( QModelIndex const & parent ) const;
|
||||
int columnCount( QModelIndex const & parent ) const;
|
||||
QVariant headerData( int section, Qt::Orientation orientation, int role ) const;
|
||||
QVariant data( QModelIndex const & index, int role ) const;
|
||||
bool setData( QModelIndex const & index, const QVariant & value, int role );
|
||||
|
||||
private:
|
||||
|
||||
Config::Programs programs;
|
||||
};
|
||||
|
||||
class ProgramTypeEditor: public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int type READ getType WRITE setType USER true)
|
||||
|
||||
public:
|
||||
ProgramTypeEditor( QWidget * widget = 0 );
|
||||
|
||||
// Returns localized name for the given program type
|
||||
static QString getNameForType( int );
|
||||
|
||||
public:
|
||||
int getType() const;
|
||||
void setType( int );
|
||||
};
|
||||
|
||||
/// A model to be projected into the paths view, according to Qt's MVC model
|
||||
class PathsModel: public QAbstractItemModel
|
||||
{
|
||||
|
@ -171,7 +220,8 @@ public:
|
|||
Config::Transliteration const &,
|
||||
Config::Forvo const & forvo,
|
||||
Config::MediaWikis const &,
|
||||
Config::WebSites const & );
|
||||
Config::WebSites const &,
|
||||
Config::Programs const &);
|
||||
|
||||
Config::Paths const & getPaths() const
|
||||
{ return pathsModel.getCurrentPaths(); }
|
||||
|
@ -185,6 +235,9 @@ public:
|
|||
Config::WebSites const & getWebSites() const
|
||||
{ return webSitesModel.getCurrentWebSites(); }
|
||||
|
||||
Config::Programs const & getPrograms() const
|
||||
{ return programsModel.getCurrentPrograms(); }
|
||||
|
||||
Config::Hunspell getHunspell() const;
|
||||
|
||||
Config::Transliteration getTransliteration() const;
|
||||
|
@ -198,8 +251,13 @@ signals:
|
|||
|
||||
private:
|
||||
Ui::Sources ui;
|
||||
|
||||
QItemDelegate * itemDelegate;
|
||||
QItemEditorFactory * itemEditorFactory;
|
||||
|
||||
MediaWikisModel mediawikisModel;
|
||||
WebSitesModel webSitesModel;
|
||||
ProgramsModel programsModel;
|
||||
PathsModel pathsModel;
|
||||
SoundDirsModel soundDirsModel;
|
||||
HunspellDictsModel hunspellDictsModel;
|
||||
|
@ -224,6 +282,9 @@ private slots:
|
|||
void on_addWebSite_clicked();
|
||||
void on_removeWebSite_clicked();
|
||||
|
||||
void on_addProgram_clicked();
|
||||
void on_removeProgram_clicked();
|
||||
|
||||
void on_rescan_clicked();
|
||||
};
|
||||
|
||||
|
|
61
sources.ui
61
sources.ui
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>665</width>
|
||||
<width>690</width>
|
||||
<height>336</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -334,6 +334,65 @@ of the appropriate groups to use them.</string>
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_6">
|
||||
<attribute name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/programs.png</normaloff>:/icons/programs.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Programs</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>Any external programs. A string %GDWORD% will be replaced with the query word. The word will also be fed into standard input.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QTreeView" name="programs"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addProgram">
|
||||
<property name="text">
|
||||
<string>&Add...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeProgram">
|
||||
<property name="text">
|
||||
<string>&Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_12">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_5">
|
||||
<attribute name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
|
|
Loading…
Reference in a new issue