mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-27 19:24:08 +00:00
Save images and sounds via context menu (issue #223)
This commit is contained in:
parent
4e799b9ed2
commit
0ac060576d
243
articleview.cc
243
articleview.cc
|
@ -11,6 +11,7 @@
|
|||
#include <QWebHistory>
|
||||
#include <QClipboard>
|
||||
#include <QKeyEvent>
|
||||
#include <QFileDialog>
|
||||
#include "folding.hh"
|
||||
#include "wstring_qt.hh"
|
||||
#include "webmultimediadownload.hh"
|
||||
|
@ -954,6 +955,221 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref,
|
|||
}
|
||||
}
|
||||
|
||||
void ArticleView::saveResource( const QUrl & url, QUrl const & ref )
|
||||
{
|
||||
resourceToSaveDownloadRequests.clear();
|
||||
resourceToSaveUrl = url;
|
||||
sptr< Dictionary::DataRequest > req;
|
||||
|
||||
if( url.scheme() == "bres" || url.scheme() == "gico" || url.scheme() == "gdau")
|
||||
{
|
||||
if ( url.host() == "search" )
|
||||
{
|
||||
// Since searches should be limited to current group, we just do them
|
||||
// here ourselves since otherwise we'd need to pass group id to netmgr
|
||||
// and it should've been having knowledge of the current groups, too.
|
||||
|
||||
unsigned currentGroup = getGroup( ref );
|
||||
|
||||
std::vector< sptr< Dictionary::Class > > const * activeDicts = 0;
|
||||
|
||||
if ( groups.size() )
|
||||
{
|
||||
for( unsigned x = 0; x < groups.size(); ++x )
|
||||
if ( groups[ x ].id == currentGroup )
|
||||
{
|
||||
activeDicts = &( groups[ x ].dictionaries );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
activeDicts = &allDictionaries;
|
||||
|
||||
if ( activeDicts )
|
||||
{
|
||||
for( unsigned x = 0; x < activeDicts->size(); ++x )
|
||||
{
|
||||
req = (*activeDicts)[ x ]->getResource(
|
||||
url.path().mid( 1 ).toUtf8().data() );
|
||||
|
||||
if ( req->isFinished() && req->dataSize() >= 0 )
|
||||
{
|
||||
// A request was instantly finished with success.
|
||||
// If we've managed to spawn some lingering requests already,
|
||||
// erase them.
|
||||
resourceToSaveDownloadRequests.clear();
|
||||
|
||||
// Handle the result
|
||||
resourceToSaveDownloadRequests.push_back( req );
|
||||
resourceToSaveDownloadFinished();
|
||||
return;
|
||||
}
|
||||
else
|
||||
if ( !req->isFinished() )
|
||||
{
|
||||
resourceToSaveDownloadRequests.push_back( req );
|
||||
connect( req.get(), SIGNAL( finished() ),
|
||||
this, SLOT( resourceToSaveDownloadFinished() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal resource download
|
||||
QString contentType;
|
||||
req = articleNetMgr.getResource( url, contentType );
|
||||
}
|
||||
}
|
||||
else
|
||||
req = new Dictionary::WebMultimediaDownload( url, articleNetMgr );
|
||||
|
||||
if( url.host().compare( "search" ) != 0 )
|
||||
{
|
||||
if ( !req.get() )
|
||||
{
|
||||
// Request failed, fail
|
||||
}
|
||||
else
|
||||
if ( req->isFinished() && req->dataSize() >= 0 )
|
||||
{
|
||||
// Have data ready, handle it
|
||||
resourceToSaveDownloadRequests.push_back( req );
|
||||
resourceToSaveDownloadFinished();
|
||||
return;
|
||||
}
|
||||
else
|
||||
if ( !req->isFinished() )
|
||||
{
|
||||
// Queue to be handled when done
|
||||
|
||||
resourceToSaveDownloadRequests.push_back( req );
|
||||
connect( req.get(), SIGNAL( finished() ),
|
||||
this, SLOT( resourceToSaveDownloadFinished() ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( resourceToSaveDownloadRequests.empty() ) // No requests were queued
|
||||
{
|
||||
emit statusBarMessage(
|
||||
tr( "ERROR: %1" ).arg( tr( "The referenced resource doesn't exist." ) ),
|
||||
10000, QPixmap( ":/icons/error.png" ) );
|
||||
return;
|
||||
}
|
||||
else
|
||||
resourceToSaveDownloadFinished(); // Check any requests finished already
|
||||
}
|
||||
|
||||
void ArticleView::resourceToSaveDownloadFinished()
|
||||
{
|
||||
if ( resourceToSaveDownloadRequests.empty() )
|
||||
return; // Stray signal
|
||||
|
||||
QByteArray resourceData;
|
||||
|
||||
// Find any finished resources
|
||||
|
||||
for( list< sptr< Dictionary::DataRequest > >::iterator i =
|
||||
resourceToSaveDownloadRequests.begin(); i != resourceToSaveDownloadRequests.end(); )
|
||||
{
|
||||
if ( (*i)->isFinished() )
|
||||
{
|
||||
if ( (*i)->dataSize() >= 0 )
|
||||
{
|
||||
// Ok, got one finished, all others are irrelevant now
|
||||
|
||||
vector< char > const & data = (*i)->getFullData();
|
||||
resourceData = QByteArray::fromRawData( data.data(), data.size() );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This one had no data. Erase it.
|
||||
resourceToSaveDownloadRequests.erase( i++ );
|
||||
}
|
||||
}
|
||||
else // Unfinished, try the next one.
|
||||
i++;
|
||||
}
|
||||
|
||||
if( !resourceData.isEmpty() )
|
||||
{
|
||||
QString fileName;
|
||||
QString savePath;
|
||||
if( cfg.resourceSavePath.isEmpty() )
|
||||
savePath = QDir::homePath();
|
||||
else
|
||||
{
|
||||
savePath = QDir::fromNativeSeparators( cfg.resourceSavePath );
|
||||
if( !QDir( savePath ).exists() )
|
||||
savePath = QDir::homePath();
|
||||
}
|
||||
|
||||
QString name = resourceToSaveUrl.path().section( '/', -1 );
|
||||
|
||||
if ( resourceToSaveUrl.scheme() == "gdau" ||
|
||||
Dictionary::WebMultimediaDownload::isAudioUrl( resourceToSaveUrl ) )
|
||||
{
|
||||
// Audio data
|
||||
if( name.indexOf( '.' ) < 0 )
|
||||
name += ".wav";
|
||||
|
||||
fileName = savePath + "/" + name;
|
||||
fileName = QFileDialog::getSaveFileName( parentWidget(), tr( "Save sound" ),
|
||||
fileName,
|
||||
tr( "Sound files (*.wav *.ogg *.mp3 *.mp4 *.aac *.flac *.mid *.wv *.ape);;All files (*.*)" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Image data
|
||||
|
||||
// Check for babylon image name
|
||||
if( name[ 0 ] == '\x1E' )
|
||||
name.remove( 0, 1 );
|
||||
if( name[ name.length() - 1 ] == '\x1F' )
|
||||
name.chop( 1 );
|
||||
|
||||
fileName = savePath + "/" + name;
|
||||
fileName = QFileDialog::getSaveFileName( parentWidget(), tr( "Save image" ),
|
||||
fileName,
|
||||
tr( "Image files (*.bmp *.jpg *.png *.tif);;All files (*.*)" ) );
|
||||
}
|
||||
|
||||
// Write data to file
|
||||
|
||||
if( !fileName.isEmpty() )
|
||||
{
|
||||
QFileInfo fileInfo( fileName );
|
||||
emit storeResourceSavePath( QDir::toNativeSeparators( fileInfo.absoluteDir().absolutePath() ) );
|
||||
QFile file( fileName );
|
||||
if ( file.open( QFile::WriteOnly ) )
|
||||
{
|
||||
file.write( resourceData.data(), resourceData.size() );
|
||||
file.close();
|
||||
}
|
||||
if( file.error() )
|
||||
{
|
||||
emit statusBarMessage(
|
||||
tr( "ERROR: %1" ).arg( tr( "Resource saving error: " ) + file.errorString() ),
|
||||
10000, QPixmap( ":/icons/error.png" ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, whatever it was, it's finished. Remove this and any other
|
||||
// requests and finish.
|
||||
|
||||
resourceToSaveDownloadRequests.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( resourceToSaveDownloadRequests.empty() )
|
||||
{
|
||||
emit statusBarMessage(
|
||||
tr( "ERROR: %1" ).arg( tr( "The referenced resource failed to download." ) ),
|
||||
10000, QPixmap( ":/icons/error.png" ) );
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleView::updateMutedContents()
|
||||
{
|
||||
QUrl currentUrl = ui.definition->url();
|
||||
|
@ -1073,6 +1289,8 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
|
|||
QAction * addWordToHistoryAction = 0;
|
||||
QAction * addHeaderToHistoryAction = 0;
|
||||
QAction * sendWordToInputLineAction = 0;
|
||||
QAction * saveImageAction = 0;
|
||||
QAction * saveSoundAction = 0;
|
||||
|
||||
QUrl targetUrl( r.linkUrl() );
|
||||
Contexts contexts;
|
||||
|
@ -1102,6 +1320,25 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
|
|||
}
|
||||
}
|
||||
|
||||
QWebElement el = r.element();
|
||||
QUrl imageUrl;
|
||||
if( !popupView && el.tagName().compare( "img", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
imageUrl = QUrl::fromPercentEncoding( el.attribute( "src" ).toLatin1() );
|
||||
if( !imageUrl.isEmpty() )
|
||||
{
|
||||
saveImageAction = new QAction( tr( "Save &image" ), &menu );
|
||||
menu.addAction( saveImageAction );
|
||||
}
|
||||
}
|
||||
|
||||
if( !popupView && ( targetUrl.scheme() == "gdau"
|
||||
|| Dictionary::WebMultimediaDownload::isAudioUrl( targetUrl ) ) )
|
||||
{
|
||||
saveSoundAction = new QAction( tr( "Save s&ound" ), &menu );
|
||||
menu.addAction( saveSoundAction );
|
||||
}
|
||||
|
||||
QString selectedText = ui.definition->selectedText();
|
||||
|
||||
if ( selectedText.size() && selectedText.size() < 60 )
|
||||
|
@ -1265,6 +1502,12 @@ void ArticleView::contextMenuRequested( QPoint const & pos )
|
|||
emit showDefinitionInNewTab( selectedText, groupComboBox->getCurrentGroup(),
|
||||
QString(), Contexts() );
|
||||
else
|
||||
if( result == saveImageAction )
|
||||
saveResource( imageUrl, ui.definition->url() );
|
||||
else
|
||||
if( result == saveSoundAction )
|
||||
saveResource( targetUrl, ui.definition->url() );
|
||||
else
|
||||
{
|
||||
if ( !popupView && result == maxDictionaryRefsAction )
|
||||
emit showDictsPane();
|
||||
|
|
|
@ -46,6 +46,9 @@ class ArticleView: public QFrame
|
|||
/// Url of the resourceDownloadRequests
|
||||
QUrl resourceDownloadUrl;
|
||||
|
||||
std::list< sptr< Dictionary::DataRequest > > resourceToSaveDownloadRequests;
|
||||
QUrl resourceToSaveUrl;
|
||||
|
||||
/// For resources opened via desktop services
|
||||
QString desktopOpenedTempFile;
|
||||
|
||||
|
@ -209,6 +212,8 @@ signals:
|
|||
|
||||
void sendWordToInputLine( QString const & word );
|
||||
|
||||
void storeResourceSavePath(QString const & );
|
||||
|
||||
public slots:
|
||||
|
||||
void on_searchPrevious_clicked();
|
||||
|
@ -237,6 +242,7 @@ private slots:
|
|||
void contextMenuRequested( QPoint const & );
|
||||
|
||||
void resourceDownloadFinished();
|
||||
void resourceToSaveDownloadFinished();
|
||||
|
||||
/// We handle pasting by attempting to define the word in clipboard.
|
||||
void pasteTriggered();
|
||||
|
@ -307,6 +313,8 @@ private:
|
|||
/// for the given group. If there are none, returns empty string.
|
||||
QString getMutedForGroup( unsigned group );
|
||||
|
||||
void saveResource( QUrl const &, QUrl const & );
|
||||
|
||||
protected:
|
||||
|
||||
// We need this to hide the search bar when we're showed
|
||||
|
|
10
config.cc
10
config.cc
|
@ -777,6 +777,9 @@ Class load() throw( exError )
|
|||
if ( !root.namedItem( "historyExportPath" ).isNull() )
|
||||
c.historyExportPath = root.namedItem( "historyExportPath" ).toElement().text();
|
||||
|
||||
if ( !root.namedItem( "resourceSavePath" ).isNull() )
|
||||
c.resourceSavePath = root.namedItem( "resourceSavePath" ).toElement().text();
|
||||
|
||||
if ( !root.namedItem( "editDictionaryCommandLine" ).isNull() )
|
||||
c.editDictionaryCommandLine = root.namedItem( "editDictionaryCommandLine" ).toElement().text();
|
||||
|
||||
|
@ -1470,6 +1473,13 @@ void save( Class const & c ) throw( exError )
|
|||
root.appendChild( opt );
|
||||
}
|
||||
|
||||
if( !c.resourceSavePath.isEmpty() )
|
||||
{
|
||||
opt = dd.createElement( "resourceSavePath" );
|
||||
opt.appendChild( dd.createTextNode( c.resourceSavePath ) );
|
||||
root.appendChild( opt );
|
||||
}
|
||||
|
||||
opt = dd.createElement( "editDictionaryCommandLine" );
|
||||
opt.appendChild( dd.createTextNode( c.editDictionaryCommandLine ) );
|
||||
root.appendChild( opt );
|
||||
|
|
|
@ -404,6 +404,7 @@ struct Class
|
|||
QByteArray dictInfoGeometry; // Geometry of "Dictionary info" window
|
||||
|
||||
QString historyExportPath; // Path for export/import history
|
||||
QString resourceSavePath; // Path to save images/audio
|
||||
|
||||
bool pinPopupWindow; // Last pin status
|
||||
|
||||
|
|
|
@ -1335,7 +1335,10 @@ ArticleView * MainWindow::createNewTab( bool switchToIt,
|
|||
this, SLOT( addWordToHistory( QString ) ) );
|
||||
|
||||
connect( view, SIGNAL( sendWordToInputLine( QString const & ) ),
|
||||
this, SLOT(sendWordToInputLine( QString const & ) ) );
|
||||
this, SLOT( sendWordToInputLine( QString const & ) ) );
|
||||
|
||||
connect( view, SIGNAL( storeResourceSavePath( QString const & ) ),
|
||||
this, SLOT( storeResourceSavePath( QString const & ) ) );
|
||||
|
||||
view->setSelectionBySingleClick( cfg.preferences.selectWordBySingleClick );
|
||||
|
||||
|
@ -3340,6 +3343,11 @@ void MainWindow::sendWordToInputLine( const QString & word )
|
|||
translateLine->setText( word );
|
||||
}
|
||||
|
||||
void MainWindow::storeResourceSavePath( const QString & newPath )
|
||||
{
|
||||
cfg.resourceSavePath = newPath;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
|
||||
bool MainWindow::winEvent( MSG * message, long * result )
|
||||
|
|
|
@ -385,6 +385,8 @@ private slots:
|
|||
|
||||
void sendWordToInputLine( QString const & word );
|
||||
|
||||
void storeResourceSavePath( QString const & );
|
||||
|
||||
signals:
|
||||
/// Set optional parts expand mode for all tabs
|
||||
void setExpandOptionalParts( bool expand );
|
||||
|
|
Loading…
Reference in a new issue