2012-02-20 21:47:14 +00:00
|
|
|
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
2009-01-28 20:55:45 +00:00
|
|
|
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
|
|
|
|
|
|
|
#include "articleview.hh"
|
2022-03-27 14:22:42 +00:00
|
|
|
#include "QtCore/qvariant.h"
|
2021-12-19 10:37:27 +00:00
|
|
|
#include "folding.hh"
|
|
|
|
#include "fulltextsearch.hh"
|
|
|
|
#include "gddebug.hh"
|
|
|
|
#include "gestures.hh"
|
|
|
|
#include "programs.hh"
|
|
|
|
#include "utils.hh"
|
|
|
|
#include "webmultimediadownload.hh"
|
2021-12-11 16:34:37 +00:00
|
|
|
#include "weburlrequestinterceptor.h"
|
2021-12-19 10:37:27 +00:00
|
|
|
#include "wildcard.hh"
|
|
|
|
#include "wstring_qt.hh"
|
|
|
|
#include <QCryptographicHash>
|
|
|
|
#include <QDebug>
|
2009-02-01 00:08:08 +00:00
|
|
|
#include <QDesktopServices>
|
2021-12-19 10:37:27 +00:00
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QKeyEvent>
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
#include <QWebChannel>
|
2021-07-06 13:01:50 +00:00
|
|
|
#include <QWebEngineHistory>
|
|
|
|
#include <QWebEngineScript>
|
|
|
|
#include <QWebEngineScriptCollection>
|
2021-12-19 10:37:27 +00:00
|
|
|
#include <QWebEngineSettings>
|
2013-05-27 11:18:13 +00:00
|
|
|
#include <assert.h>
|
2021-12-19 10:37:27 +00:00
|
|
|
#include <map>
|
2022-02-27 05:17:37 +00:00
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(6,0,0))
|
2021-12-28 13:59:49 +00:00
|
|
|
#include <QWebEngineContextMenuData>
|
2022-02-27 05:17:37 +00:00
|
|
|
#endif
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
|
2022-02-27 14:42:40 +00:00
|
|
|
#include <QtCore5Compat/QRegExp>
|
2022-02-27 05:17:37 +00:00
|
|
|
#include <QWebEngineContextMenuRequest>
|
|
|
|
#include <QWebEngineFindTextResult>
|
|
|
|
#endif
|
2010-11-14 15:38:41 +00:00
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
#include <windows.h>
|
2012-09-18 23:01:31 +00:00
|
|
|
#include <QPainter>
|
2010-11-14 15:38:41 +00:00
|
|
|
#endif
|
2012-09-18 23:01:31 +00:00
|
|
|
|
2010-01-02 18:16:22 +00:00
|
|
|
#include <QBuffer>
|
2010-03-29 13:13:29 +00:00
|
|
|
|
2014-05-12 13:53:13 +00:00
|
|
|
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
2013-05-07 13:39:35 +00:00
|
|
|
#include "speechclient.hh"
|
|
|
|
#endif
|
|
|
|
|
2022-01-08 06:51:24 +00:00
|
|
|
#include "globalbroadcaster.h"
|
2009-04-12 16:22:42 +00:00
|
|
|
using std::map;
|
2009-03-26 19:00:08 +00:00
|
|
|
using std::list;
|
|
|
|
|
2014-04-24 14:12:00 +00:00
|
|
|
/// AccentMarkHandler class
|
|
|
|
///
|
|
|
|
/// Remove accent marks from text
|
|
|
|
/// and mirror position in normalized text to original text
|
|
|
|
|
|
|
|
class AccentMarkHandler
|
|
|
|
{
|
2018-04-10 14:49:52 +00:00
|
|
|
protected:
|
2014-04-24 14:12:00 +00:00
|
|
|
QString normalizedString;
|
|
|
|
QVector< int > accentMarkPos;
|
|
|
|
public:
|
2018-04-10 14:49:52 +00:00
|
|
|
AccentMarkHandler()
|
|
|
|
{}
|
|
|
|
virtual ~AccentMarkHandler()
|
|
|
|
{}
|
2014-04-24 14:12:00 +00:00
|
|
|
static QChar accentMark()
|
|
|
|
{ return QChar( 0x301 ); }
|
|
|
|
|
|
|
|
/// Create text without accent marks
|
|
|
|
/// and store mark positions
|
2018-04-10 14:49:52 +00:00
|
|
|
virtual void setText( QString const & baseString )
|
2014-04-24 14:12:00 +00:00
|
|
|
{
|
|
|
|
accentMarkPos.clear();
|
|
|
|
normalizedString.clear();
|
|
|
|
int pos = 0;
|
|
|
|
QChar mark = accentMark();
|
|
|
|
|
|
|
|
for( int x = 0; x < baseString.length(); x++ )
|
|
|
|
{
|
|
|
|
if( baseString.at( x ) == mark )
|
|
|
|
{
|
|
|
|
accentMarkPos.append( pos );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
normalizedString.append( baseString.at( x ) );
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return text without accent marks
|
|
|
|
QString const & normalizedText() const
|
|
|
|
{ return normalizedString; }
|
|
|
|
|
|
|
|
/// Convert position into position in original text
|
|
|
|
int mirrorPosition( int const & pos ) const
|
|
|
|
{
|
|
|
|
int newPos = pos;
|
|
|
|
for( int x = 0; x < accentMarkPos.size(); x++ )
|
|
|
|
{
|
|
|
|
if( accentMarkPos.at( x ) < pos )
|
|
|
|
newPos++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return newPos;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/// End of DslAccentMark class
|
2010-01-02 18:16:22 +00:00
|
|
|
|
2018-04-10 14:49:52 +00:00
|
|
|
/// DiacriticsHandler class
|
|
|
|
///
|
|
|
|
/// Remove diacritics from text
|
|
|
|
/// and mirror position in normalized text to original text
|
|
|
|
|
|
|
|
class DiacriticsHandler : public AccentMarkHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DiacriticsHandler()
|
|
|
|
{}
|
|
|
|
~DiacriticsHandler()
|
|
|
|
{}
|
|
|
|
|
|
|
|
/// Create text without diacriticss
|
|
|
|
/// and store diacritic marks positions
|
2022-12-29 07:07:40 +00:00
|
|
|
void setText( QString const & baseString ) override
|
2018-04-10 14:49:52 +00:00
|
|
|
{
|
|
|
|
accentMarkPos.clear();
|
|
|
|
normalizedString.clear();
|
|
|
|
|
|
|
|
gd::wstring baseText = gd::toWString( baseString );
|
|
|
|
gd::wstring normText;
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
normText.reserve( baseText.size() );
|
|
|
|
|
|
|
|
gd::wchar const * nextChar = baseText.data();
|
|
|
|
size_t consumed;
|
|
|
|
|
|
|
|
for( size_t left = baseText.size(); left; )
|
|
|
|
{
|
|
|
|
if( *nextChar >= 0x10000U )
|
|
|
|
{
|
|
|
|
// Will be translated into surrogate pair
|
|
|
|
normText.push_back( *nextChar );
|
|
|
|
pos += 2;
|
|
|
|
nextChar++; left--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
gd::wchar ch = Folding::foldedDiacritic( nextChar, left, consumed );
|
|
|
|
|
|
|
|
if( Folding::isCombiningMark( ch ) )
|
|
|
|
{
|
|
|
|
accentMarkPos.append( pos );
|
|
|
|
nextChar++; left--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( consumed > 1 )
|
|
|
|
{
|
|
|
|
for( size_t i = 1; i < consumed; i++ )
|
|
|
|
accentMarkPos.append( pos );
|
|
|
|
}
|
|
|
|
|
|
|
|
normText.push_back( ch );
|
|
|
|
pos += 1;
|
|
|
|
nextChar += consumed;
|
|
|
|
left -= consumed;
|
|
|
|
}
|
|
|
|
normalizedString = gd::toQString( normText );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/// End of DiacriticsHandler class
|
2021-11-24 14:38:37 +00:00
|
|
|
|
|
|
|
void ArticleView::emitJavascriptFinished(){
|
|
|
|
emit notifyJavascriptFinished();
|
|
|
|
}
|
2013-05-29 07:03:37 +00:00
|
|
|
|
2021-06-29 08:59:16 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
char const * const scrollToPrefix = "gdfrom-";
|
|
|
|
|
|
|
|
bool isScrollTo( QString const & id )
|
|
|
|
{
|
|
|
|
return id.startsWith( scrollToPrefix );
|
|
|
|
}
|
|
|
|
|
|
|
|
QString dictionaryIdFromScrollTo( QString const & scrollTo )
|
|
|
|
{
|
|
|
|
Q_ASSERT( isScrollTo( scrollTo ) );
|
|
|
|
const int scrollToPrefixLength = 7;
|
|
|
|
return scrollTo.mid( scrollToPrefixLength );
|
|
|
|
}
|
|
|
|
|
2022-07-02 13:36:19 +00:00
|
|
|
QString searchStatusMessageNoMatches()
|
|
|
|
{
|
|
|
|
return ArticleView::tr( "Phrase not found" );
|
|
|
|
}
|
|
|
|
|
|
|
|
QString searchStatusMessage( int activeMatch, int matchCount )
|
|
|
|
{
|
|
|
|
Q_ASSERT( matchCount > 0 );
|
|
|
|
Q_ASSERT( activeMatch > 0 );
|
|
|
|
Q_ASSERT( activeMatch <= matchCount );
|
|
|
|
return ArticleView::tr( "%1 of %2 matches" ).arg( activeMatch ).arg( matchCount );
|
|
|
|
}
|
|
|
|
|
2021-06-29 08:59:16 +00:00
|
|
|
} // unnamed namespace
|
|
|
|
|
|
|
|
QString ArticleView::scrollToFromDictionaryId( QString const & dictionaryId )
|
|
|
|
{
|
|
|
|
Q_ASSERT( !isScrollTo( dictionaryId ) );
|
|
|
|
return scrollToPrefix + dictionaryId;
|
|
|
|
}
|
|
|
|
|
2022-02-16 14:50:44 +00:00
|
|
|
ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, AudioPlayerPtr const & audioPlayer_,
|
2009-04-12 16:22:42 +00:00
|
|
|
std::vector< sptr< Dictionary::Class > > const & allDictionaries_,
|
2022-02-16 14:50:44 +00:00
|
|
|
Instances::Groups const & groups_, bool popupView_, Config::Class const & cfg_,
|
2023-03-16 11:55:47 +00:00
|
|
|
QAction & openSearchAction_,
|
|
|
|
QLineEdit const * translateLine_,
|
|
|
|
QAction * dictionaryBarToggled_,
|
|
|
|
GroupComboBox const * groupComboBox_
|
|
|
|
):
|
2023-03-17 09:42:44 +00:00
|
|
|
QWidget( parent ),
|
2009-01-28 20:55:45 +00:00
|
|
|
articleNetMgr( nm ),
|
2018-03-21 17:49:34 +00:00
|
|
|
audioPlayer( audioPlayer_ ),
|
2009-04-12 16:22:42 +00:00
|
|
|
allDictionaries( allDictionaries_ ),
|
2009-01-28 20:55:45 +00:00
|
|
|
groups( groups_ ),
|
2009-04-10 21:07:03 +00:00
|
|
|
popupView( popupView_ ),
|
2009-05-12 10:52:11 +00:00
|
|
|
cfg( cfg_ ),
|
2009-05-14 19:27:19 +00:00
|
|
|
pasteAction( this ),
|
2009-05-15 14:11:54 +00:00
|
|
|
articleUpAction( this ),
|
|
|
|
articleDownAction( this ),
|
2009-05-29 22:04:43 +00:00
|
|
|
goBackAction( this ),
|
|
|
|
goForwardAction( this ),
|
2013-01-18 14:37:24 +00:00
|
|
|
selectCurrentArticleAction( this ),
|
2013-05-15 13:52:47 +00:00
|
|
|
copyAsTextAction( this ),
|
2013-05-30 02:18:28 +00:00
|
|
|
inspectAction( this ),
|
2014-04-16 16:18:28 +00:00
|
|
|
openSearchAction( openSearchAction_ ),
|
2009-05-16 11:14:43 +00:00
|
|
|
searchIsOpened( false ),
|
2009-09-23 18:44:38 +00:00
|
|
|
dictionaryBarToggled( dictionaryBarToggled_ ),
|
2014-04-22 13:47:02 +00:00
|
|
|
groupComboBox( groupComboBox_ ),
|
2023-03-16 11:55:47 +00:00
|
|
|
translateLine( translateLine_ ),
|
2014-04-22 13:47:02 +00:00
|
|
|
ftsSearchIsOpened( false ),
|
|
|
|
ftsSearchMatchCase( false ),
|
|
|
|
ftsPosition( 0 )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
// setup GUI
|
|
|
|
webview = new ArticleWebView( this );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
// fts Search Panel
|
|
|
|
ftsSearchPanel = new QWidget( this );
|
|
|
|
|
|
|
|
auto * ftsLayout = new QHBoxLayout( ftsSearchPanel );
|
|
|
|
ftsSearchPrevious = new QPushButton( ftsSearchPanel );
|
|
|
|
ftsSearchNext = new QPushButton( ftsSearchPanel );
|
|
|
|
ftsSearchStatusLabel = new QLabel( ftsSearchPanel );
|
|
|
|
|
|
|
|
ftsLayout->addWidget( ftsSearchPrevious );
|
|
|
|
ftsLayout->addWidget( ftsSearchNext );
|
|
|
|
ftsLayout->addWidget( ftsSearchStatusLabel );
|
|
|
|
|
|
|
|
ftsSearchPrevious->setIcon( QIcon( ":/icons/previous.svg" ) );
|
|
|
|
ftsSearchNext->setIcon( QIcon( ":/icons/next.svg" ) );
|
|
|
|
|
|
|
|
ftsSearchPrevious->setText( tr( "&Previous" ) );
|
|
|
|
ftsSearchNext->setText( tr( "&Next" ) );
|
|
|
|
|
|
|
|
ftsLayout->addStretch();
|
|
|
|
|
|
|
|
// Search Panel
|
|
|
|
searchPanel = new QWidget( this );
|
|
|
|
|
|
|
|
auto * searchLabel = new QLabel( tr( "Find:" ) );
|
|
|
|
|
|
|
|
searchText = new QLineEdit( searchPanel );
|
|
|
|
|
|
|
|
searchCloseButton = new QPushButton( searchPanel );
|
|
|
|
searchCloseButton->setIcon( QIcon( ":/icons/closetab.svg" ) );
|
|
|
|
|
|
|
|
searchPrevious = new QPushButton( searchPanel );
|
|
|
|
searchPrevious->setIcon( QIcon( ":/icons/previous.svg" ) );
|
|
|
|
searchPrevious->setText( tr( "&Previous" ) );
|
|
|
|
|
|
|
|
searchNext = new QPushButton( searchPanel );
|
|
|
|
searchNext->setIcon( QIcon( ":/icons/next.svg" ) );
|
|
|
|
searchNext->setText( tr( "&Next" ) );
|
|
|
|
|
|
|
|
highlightAllButton = new QCheckBox( searchPanel );
|
|
|
|
highlightAllButton->setIcon( QIcon( ":/icons/highlighter.png" ) );
|
|
|
|
highlightAllButton->setText( tr( "Highlight &all" ) );
|
|
|
|
highlightAllButton->setChecked( true );
|
|
|
|
|
|
|
|
searchCaseSensitive = new QCheckBox( searchPanel );
|
|
|
|
searchCaseSensitive->setText( tr( "&Case Sensitive" ) );
|
|
|
|
|
|
|
|
auto * searchEditRow = new QHBoxLayout;
|
|
|
|
searchEditRow->addWidget( searchLabel );
|
|
|
|
searchEditRow->addWidget( searchText );
|
|
|
|
searchEditRow->addWidget( searchCloseButton );
|
|
|
|
|
|
|
|
auto * searchButtonsRow = new QHBoxLayout;
|
|
|
|
searchButtonsRow->addWidget( searchPrevious );
|
|
|
|
searchButtonsRow->addWidget( searchNext );
|
|
|
|
searchButtonsRow->addWidget( highlightAllButton );
|
|
|
|
searchButtonsRow->addWidget( searchCaseSensitive );
|
|
|
|
searchButtonsRow->addStretch();
|
|
|
|
|
|
|
|
auto * searchPanelLayout = new QVBoxLayout( searchPanel );
|
|
|
|
searchPanelLayout->addLayout( searchEditRow );
|
|
|
|
searchPanelLayout->addLayout( searchButtonsRow );
|
|
|
|
|
|
|
|
// Combine Layouts
|
|
|
|
auto * mainLayout = new QVBoxLayout( this );
|
|
|
|
mainLayout->addWidget( webview );
|
|
|
|
mainLayout->addWidget( ftsSearchPanel );
|
|
|
|
mainLayout->addWidget( searchPanel );
|
|
|
|
|
|
|
|
webview->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
|
|
|
ftsSearchPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
|
|
|
|
|
|
|
|
searchPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
|
|
|
|
|
|
|
|
mainLayout->setContentsMargins( 0, 0, 0, 0 );
|
|
|
|
|
|
|
|
// end UI setup
|
|
|
|
|
|
|
|
connect( searchPrevious, &QPushButton::clicked, this, &ArticleView::on_searchPrevious_clicked );
|
|
|
|
connect( searchNext, &QPushButton::clicked, this, &ArticleView::on_searchNext_clicked );
|
|
|
|
connect( searchCloseButton, &QPushButton::clicked, this, &ArticleView::on_searchCloseButton_clicked );
|
|
|
|
connect( searchCaseSensitive, &QPushButton::clicked, this, &ArticleView::on_searchCaseSensitive_clicked );
|
|
|
|
connect( highlightAllButton, &QPushButton::clicked, this, &ArticleView::on_highlightAllButton_clicked );
|
|
|
|
connect( searchText, &QLineEdit::textEdited, this, &ArticleView::on_searchText_textEdited );
|
|
|
|
connect( searchText, &QLineEdit::returnPressed, this, &ArticleView::on_searchText_returnPressed );
|
|
|
|
connect( ftsSearchNext, &QPushButton::clicked, this, &ArticleView::on_ftsSearchNext_clicked );
|
|
|
|
connect( ftsSearchPrevious, &QPushButton::clicked, this, &ArticleView::on_ftsSearchPrevious_clicked );
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
webview->setUp( const_cast< Config::Class * >( &cfg ) );
|
2013-05-30 02:18:28 +00:00
|
|
|
|
2009-05-29 22:04:43 +00:00
|
|
|
goBackAction.setShortcut( QKeySequence( "Alt+Left" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &goBackAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &goBackAction, &QAction::triggered, this, &ArticleView::back );
|
2009-05-15 14:24:37 +00:00
|
|
|
|
2009-05-29 22:04:43 +00:00
|
|
|
goForwardAction.setShortcut( QKeySequence( "Alt+Right" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &goForwardAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &goForwardAction, &QAction::triggered, this, &ArticleView::forward );
|
2009-05-15 14:24:37 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->pageAction( QWebEnginePage::Copy )->setShortcut( QKeySequence::Copy );
|
|
|
|
webview->addAction( webview->pageAction( QWebEnginePage::Copy ) );
|
2009-04-03 21:57:23 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
QAction * selectAll = webview->pageAction( QWebEnginePage::SelectAll );
|
2021-08-21 01:41:40 +00:00
|
|
|
selectAll->setShortcut( QKeySequence::SelectAll );
|
|
|
|
selectAll->setShortcutContext( Qt::WidgetWithChildrenShortcut );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( selectAll );
|
2013-01-18 13:22:24 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->setContextMenuPolicy( Qt::CustomContextMenu );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview, &QWebEngineView::loadFinished, this, &ArticleView::loadFinished );
|
2021-12-19 10:37:27 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview, &QWebEngineView::loadProgress, this, &ArticleView::loadProgress );
|
|
|
|
connect( webview, &ArticleWebView::linkClicked, this, &ArticleView::linkClicked );
|
2022-01-20 12:34:14 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview->page(), &QWebEnginePage::titleChanged, this, &ArticleView::handleTitleChanged );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview->page(), &QWebEnginePage::urlChanged, this, &ArticleView::handleUrlChanged );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview, &QWidget::customContextMenuRequested, this, &ArticleView::contextMenuRequested );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview->page(), SIGNAL( linkHovered( const QString & ) ), this, SLOT( linkHovered( const QString & ) ) );
|
2011-07-02 13:04:49 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview, &ArticleWebView::doubleClicked, this, &ArticleView::doubleClicked );
|
2010-04-08 20:37:59 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
pasteAction.setShortcut( QKeySequence::Paste );
|
|
|
|
webview->addAction( &pasteAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &pasteAction, &QAction::triggered, this, &ArticleView::pasteTriggered );
|
2009-05-12 10:52:11 +00:00
|
|
|
|
2009-05-15 14:11:54 +00:00
|
|
|
articleUpAction.setShortcut( QKeySequence( "Alt+Up" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &articleUpAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &articleUpAction, &QAction::triggered, this, &ArticleView::moveOneArticleUp );
|
2009-05-15 14:11:54 +00:00
|
|
|
|
|
|
|
articleDownAction.setShortcut( QKeySequence( "Alt+Down" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &articleDownAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &articleDownAction, &QAction::triggered, this, &ArticleView::moveOneArticleDown );
|
2009-05-15 14:11:54 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &openSearchAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &openSearchAction, &QAction::triggered, this, &ArticleView::openSearch );
|
2009-05-16 11:14:43 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
selectCurrentArticleAction.setShortcut( QKeySequence( "Ctrl+Shift+A" ) );
|
2013-01-18 14:37:24 +00:00
|
|
|
selectCurrentArticleAction.setText( tr( "Select Current Article" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &selectCurrentArticleAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( &selectCurrentArticleAction, &QAction::triggered, this, &ArticleView::selectCurrentArticle );
|
2013-01-18 14:37:24 +00:00
|
|
|
|
2013-05-15 13:52:47 +00:00
|
|
|
copyAsTextAction.setShortcut( QKeySequence( "Ctrl+Shift+C" ) );
|
|
|
|
copyAsTextAction.setText( tr( "Copy as text" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( ©AsTextAction );
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( ©AsTextAction, &QAction::triggered, this, &ArticleView::copyAsText );
|
2013-05-15 13:52:47 +00:00
|
|
|
|
2013-05-30 02:18:28 +00:00
|
|
|
inspectAction.setShortcut( QKeySequence( Qt::Key_F12 ) );
|
|
|
|
inspectAction.setText( tr( "Inspect" ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->addAction( &inspectAction );
|
2021-11-30 03:40:57 +00:00
|
|
|
|
2022-02-17 17:13:01 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( &inspectAction, &QAction::triggered, this, &ArticleView::inspectElement );
|
2013-05-30 02:18:28 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->installEventFilter( this );
|
|
|
|
searchPanel->installEventFilter( this );
|
|
|
|
ftsSearchPanel->installEventFilter( this );
|
2009-05-12 13:25:18 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
QWebEngineSettings * settings = webview->settings();
|
|
|
|
settings->setUnknownUrlSchemePolicy( QWebEngineSettings::UnknownUrlSchemePolicy::DisallowUnknownUrlSchemes );
|
2022-02-27 07:49:29 +00:00
|
|
|
#if( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
|
2022-05-17 13:24:30 +00:00
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::LocalContentCanAccessRemoteUrls, true );
|
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::LocalContentCanAccessFileUrls, true );
|
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::ErrorPageEnabled, false );
|
2022-04-26 12:21:45 +00:00
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::PluginsEnabled, cfg.preferences.enableWebPlugins );
|
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false );
|
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true );
|
2022-05-24 13:40:53 +00:00
|
|
|
settings->defaultSettings()->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false );
|
2022-02-27 07:49:29 +00:00
|
|
|
#else
|
2022-05-17 13:24:30 +00:00
|
|
|
settings->setAttribute( QWebEngineSettings::LocalContentCanAccessRemoteUrls, true );
|
|
|
|
settings->setAttribute( QWebEngineSettings::LocalContentCanAccessFileUrls, true );
|
|
|
|
settings->setAttribute( QWebEngineSettings::ErrorPageEnabled, false );
|
2022-02-27 07:49:29 +00:00
|
|
|
settings->setAttribute( QWebEngineSettings::PluginsEnabled, cfg.preferences.enableWebPlugins );
|
|
|
|
settings->setAttribute( QWebEngineSettings::PlaybackRequiresUserGesture, false );
|
2022-04-26 12:21:45 +00:00
|
|
|
settings->setAttribute( QWebEngineSettings::JavascriptCanAccessClipboard, true );
|
2022-05-24 13:40:53 +00:00
|
|
|
settings->setAttribute( QWebEngineSettings::PrintElementBackgrounds, false );
|
2022-05-08 08:00:08 +00:00
|
|
|
#endif
|
2012-09-16 10:19:47 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->load( QUrl( "gdlookup://localhost?word=(untitled)&blank=1" ) );
|
2022-08-23 11:09:38 +00:00
|
|
|
|
2012-09-16 10:19:47 +00:00
|
|
|
expandOptionalParts = cfg.preferences.alwaysExpandOptionalParts;
|
2014-02-04 13:35:42 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->grabGesture( Gestures::GDPinchGestureType );
|
|
|
|
webview->grabGesture( Gestures::GDSwipeGestureType );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
|
|
|
// Variable name for store current selection range
|
|
|
|
rangeVarName = QString( "sr_%1" ).arg( QString::number( (quint64)this, 16 ) );
|
2022-01-04 13:01:16 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( GlobalBroadcaster::instance(), &GlobalBroadcaster::dictionaryChanges, this, &ArticleView::setActiveDictIds );
|
2022-01-08 06:51:24 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( GlobalBroadcaster::instance(), &GlobalBroadcaster::dictionaryClear, this, &ArticleView::dictionaryClear );
|
2022-07-16 22:54:53 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
channel = new QWebChannel( webview->page() );
|
2022-01-19 23:29:02 +00:00
|
|
|
agent = new ArticleViewAgent(this);
|
|
|
|
attachWebChannelToHtml();
|
2022-05-21 06:03:26 +00:00
|
|
|
ankiConnector = new AnkiConnector( this, cfg );
|
|
|
|
connect( ankiConnector,
|
|
|
|
&AnkiConnector::errorText,
|
|
|
|
this,
|
|
|
|
[ this ]( QString const & errorText ) { emit statusBarMessage( errorText ); } );
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2012-12-27 20:32:16 +00:00
|
|
|
// explicitly report the minimum size, to avoid
|
|
|
|
// sidebar widgets' improper resize during restore
|
2023-03-17 09:42:44 +00:00
|
|
|
QSize ArticleView::minimumSizeHint() const { return searchPanel->minimumSizeHint(); }
|
2012-12-27 20:32:16 +00:00
|
|
|
|
2009-05-14 19:27:19 +00:00
|
|
|
void ArticleView::setGroupComboBox( GroupComboBox const * g )
|
|
|
|
{
|
|
|
|
groupComboBox = g;
|
|
|
|
}
|
|
|
|
|
2009-02-06 15:37:37 +00:00
|
|
|
ArticleView::~ArticleView()
|
|
|
|
{
|
2009-02-08 21:54:19 +00:00
|
|
|
cleanupTemp();
|
2018-03-22 17:07:10 +00:00
|
|
|
audioPlayer->stop();
|
2022-01-23 03:22:40 +00:00
|
|
|
//channel->deregisterObject(this);
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->ungrabGesture( Gestures::GDPinchGestureType );
|
|
|
|
webview->ungrabGesture( Gestures::GDSwipeGestureType );
|
2009-02-06 15:37:37 +00:00
|
|
|
}
|
|
|
|
|
2021-06-10 16:13:11 +00:00
|
|
|
void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned group,
|
2009-05-29 19:48:50 +00:00
|
|
|
QString const & scrollTo,
|
2015-10-28 19:56:58 +00:00
|
|
|
Contexts const & contexts_ )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2022-02-10 12:07:05 +00:00
|
|
|
currentWord = phrase.phrase.trimmed();
|
2022-11-04 13:25:55 +00:00
|
|
|
if( currentWord.isEmpty() )
|
|
|
|
return;
|
2022-11-04 13:23:42 +00:00
|
|
|
historyMode = false;
|
2022-01-08 13:45:10 +00:00
|
|
|
currentActiveDictIds.clear();
|
2013-06-23 09:53:10 +00:00
|
|
|
// first, let's stop the player
|
2018-03-22 17:07:10 +00:00
|
|
|
audioPlayer->stop();
|
2013-06-23 09:53:10 +00:00
|
|
|
|
2013-05-31 04:20:25 +00:00
|
|
|
QUrl req;
|
2015-10-28 19:56:58 +00:00
|
|
|
Contexts contexts( contexts_ );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
|
|
|
req.setScheme( "gdlookup" );
|
|
|
|
req.setHost( "localhost" );
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "word", phrase.phrase );
|
2021-06-10 16:13:11 +00:00
|
|
|
if ( !phrase.punctuationSuffix.isEmpty() )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "punctuation_suffix", phrase.punctuationSuffix );
|
|
|
|
Utils::Url::addQueryItem( req, "group", QString::number( group ) );
|
2018-06-13 16:00:42 +00:00
|
|
|
if( cfg.preferences.ignoreDiacritics )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "ignore_diacritics", "1" );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2009-05-11 19:14:28 +00:00
|
|
|
if ( scrollTo.size() )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "scrollto", scrollTo );
|
2009-05-29 19:48:50 +00:00
|
|
|
|
2022-06-03 04:32:27 +00:00
|
|
|
if( delayedHighlightText.size() )
|
|
|
|
{
|
2022-06-01 15:11:41 +00:00
|
|
|
Utils::Url::addQueryItem( req, "regexp", delayedHighlightText );
|
2022-06-03 04:32:27 +00:00
|
|
|
delayedHighlightText.clear();
|
|
|
|
}
|
2022-06-01 15:11:41 +00:00
|
|
|
|
2015-10-28 19:56:58 +00:00
|
|
|
Contexts::Iterator pos = contexts.find( "gdanchor" );
|
|
|
|
if( pos != contexts.end() )
|
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "gdanchor", contexts[ "gdanchor" ] );
|
2015-10-28 19:56:58 +00:00
|
|
|
contexts.erase( pos );
|
|
|
|
}
|
|
|
|
|
2009-05-29 19:48:50 +00:00
|
|
|
if ( contexts.size() )
|
|
|
|
{
|
|
|
|
QBuffer buf;
|
|
|
|
|
|
|
|
buf.open( QIODevice::WriteOnly );
|
|
|
|
|
|
|
|
QDataStream stream( &buf );
|
|
|
|
|
|
|
|
stream << contexts;
|
|
|
|
|
|
|
|
buf.close();
|
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "contexts", QString::fromLatin1( buf.buffer().toBase64() ) );
|
2009-05-29 19:48:50 +00:00
|
|
|
}
|
2009-05-11 19:14:28 +00:00
|
|
|
|
2009-09-23 18:44:38 +00:00
|
|
|
QString mutedDicts = getMutedForGroup( group );
|
|
|
|
|
|
|
|
if ( mutedDicts.size() )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "muted", mutedDicts );
|
2009-09-23 18:44:38 +00:00
|
|
|
|
2022-06-12 11:26:15 +00:00
|
|
|
// Update headwords history
|
2021-06-10 16:13:11 +00:00
|
|
|
emit sendWordToHistory( phrase.phrase );
|
2009-05-11 22:25:22 +00:00
|
|
|
|
2010-04-27 07:49:37 +00:00
|
|
|
// Any search opened is probably irrelevant now
|
|
|
|
closeSearch();
|
|
|
|
|
2012-09-16 10:19:47 +00:00
|
|
|
emit setExpandMode( expandOptionalParts );
|
|
|
|
|
2022-06-12 11:26:15 +00:00
|
|
|
load( req );
|
2009-05-11 22:25:22 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
//QApplication::setOverrideCursor( Qt::WaitCursor );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->setCursor( Qt::WaitCursor );
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2021-06-10 16:13:11 +00:00
|
|
|
void ArticleView::showDefinition( QString const & word, unsigned group,
|
|
|
|
QString const & scrollTo,
|
|
|
|
Contexts const & contexts_ )
|
|
|
|
{
|
|
|
|
showDefinition( Config::InputPhrase::fromPhrase( word ), group, scrollTo, contexts_ );
|
|
|
|
}
|
|
|
|
|
2014-04-22 13:47:02 +00:00
|
|
|
void ArticleView::showDefinition( QString const & word, QStringList const & dictIDs,
|
2018-04-10 14:49:52 +00:00
|
|
|
QRegExp const & searchRegExp, unsigned group,
|
|
|
|
bool ignoreDiacritics )
|
2014-04-16 16:18:28 +00:00
|
|
|
{
|
|
|
|
if( dictIDs.isEmpty() )
|
|
|
|
return;
|
2022-02-10 12:07:05 +00:00
|
|
|
currentWord = word.trimmed();
|
2022-11-04 13:25:55 +00:00
|
|
|
if( currentWord.isEmpty() )
|
|
|
|
return;
|
2022-11-04 13:23:42 +00:00
|
|
|
historyMode = false;
|
2014-04-16 16:18:28 +00:00
|
|
|
// first, let's stop the player
|
2018-03-22 17:07:10 +00:00
|
|
|
audioPlayer->stop();
|
2014-04-16 16:18:28 +00:00
|
|
|
|
|
|
|
QUrl req;
|
|
|
|
|
|
|
|
req.setScheme( "gdlookup" );
|
|
|
|
req.setHost( "localhost" );
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "word", word );
|
|
|
|
Utils::Url::addQueryItem( req, "dictionaries", dictIDs.join( ",") );
|
|
|
|
Utils::Url::addQueryItem( req, "regexp", searchRegExp.pattern() );
|
2022-03-20 11:27:35 +00:00
|
|
|
if( searchRegExp.caseSensitivity() == Qt::CaseSensitive )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "matchcase", "1" );
|
2022-03-20 11:27:35 +00:00
|
|
|
if( searchRegExp.patternSyntax() == QRegExp::WildcardUnix )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "wildcards", "1" );
|
|
|
|
Utils::Url::addQueryItem( req, "group", QString::number( group ) );
|
2018-04-10 14:49:52 +00:00
|
|
|
if( ignoreDiacritics )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( req, "ignore_diacritics", "1" );
|
2014-04-16 16:18:28 +00:00
|
|
|
|
2022-06-12 11:26:15 +00:00
|
|
|
// Update headwords history
|
2014-04-16 16:18:28 +00:00
|
|
|
emit sendWordToHistory( word );
|
|
|
|
|
|
|
|
// Any search opened is probably irrelevant now
|
|
|
|
closeSearch();
|
|
|
|
|
|
|
|
// Clear highlight all button selection
|
2023-03-17 09:42:44 +00:00
|
|
|
highlightAllButton->setChecked( false );
|
2014-04-16 16:18:28 +00:00
|
|
|
|
|
|
|
emit setExpandMode( expandOptionalParts );
|
|
|
|
|
2022-06-12 11:26:15 +00:00
|
|
|
load( req );
|
2014-04-16 16:18:28 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->setCursor( Qt::WaitCursor );
|
2014-04-16 16:18:28 +00:00
|
|
|
}
|
|
|
|
|
2023-03-16 11:55:47 +00:00
|
|
|
void ArticleView::sendToAnki(QString const & word, QString const & text, QString const & sentence ){
|
|
|
|
ankiConnector->sendToAnki(word,text,sentence);
|
2022-05-21 06:03:26 +00:00
|
|
|
}
|
|
|
|
|
2009-02-08 16:50:18 +00:00
|
|
|
void ArticleView::showAnticipation()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->setHtml( "" );
|
|
|
|
webview->setCursor( Qt::WaitCursor );
|
2009-02-08 16:50:18 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::inspectElement() { emit inspectSignal( webview->page() ); }
|
2022-02-17 17:13:01 +00:00
|
|
|
|
2022-02-04 13:19:32 +00:00
|
|
|
void ArticleView::loadFinished( bool result )
|
2009-02-08 16:50:18 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->setFocus();
|
2022-05-20 09:18:38 +00:00
|
|
|
setZoomFactor( cfg.preferences.zoomFactor );
|
2023-03-17 09:42:44 +00:00
|
|
|
QUrl url = webview->url();
|
2022-07-28 13:23:52 +00:00
|
|
|
qDebug() << "article view loaded url:" << url.url().left( 200 ) << result;
|
|
|
|
|
|
|
|
if( url.url() == "about:blank" )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !result )
|
|
|
|
{
|
|
|
|
qWarning() << "article loaded unsuccessful";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-11 13:50:40 +00:00
|
|
|
if( cfg.preferences.autoScrollToTargetArticle )
|
2009-05-29 19:48:50 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
QString const scrollTo = Utils::Url::queryItemValue( url, "scrollto" );
|
2021-06-29 08:59:16 +00:00
|
|
|
if( isScrollTo( scrollTo ) )
|
|
|
|
{
|
|
|
|
setCurrentArticle( scrollTo, true );
|
|
|
|
}
|
2009-05-29 19:48:50 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->unsetCursor();
|
2012-09-16 10:19:47 +00:00
|
|
|
|
2013-06-03 17:14:05 +00:00
|
|
|
// Expand collapsed article if only one loaded
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript( QString( "gdCheckArticlesNumber();" ) );
|
2013-06-03 17:14:05 +00:00
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
if( !Utils::Url::queryItemValue( url, "gdanchor" ).isEmpty() )
|
2015-10-28 19:56:58 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
QString anchor = QUrl::fromPercentEncoding( Utils::Url::encodedQueryItemValue( url, "gdanchor" ) );
|
2015-10-28 19:56:58 +00:00
|
|
|
|
|
|
|
// Find GD anchor on page
|
|
|
|
|
|
|
|
int n = anchor.indexOf( '_' );
|
2016-05-11 14:41:13 +00:00
|
|
|
if( n == 33 )
|
|
|
|
// MDict pattern: ("g" + dictionary ID (33 chars total))_(articleID(quint64, hex))_(original anchor)
|
2015-10-28 19:56:58 +00:00
|
|
|
n = anchor.indexOf( '_', n + 1 );
|
2015-10-30 18:01:38 +00:00
|
|
|
else
|
|
|
|
n = 0;
|
2015-10-28 19:56:58 +00:00
|
|
|
|
|
|
|
if( n > 0 )
|
|
|
|
{
|
|
|
|
QString originalAnchor = anchor.mid( n + 1 );
|
|
|
|
|
2021-07-06 13:01:50 +00:00
|
|
|
int end = originalAnchor.indexOf('_');
|
|
|
|
QString hash=originalAnchor.left(end);
|
|
|
|
url.clear();
|
|
|
|
url.setFragment(hash);
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript(
|
|
|
|
QString( "window.location.hash = \"%1\"" ).arg( QString::fromUtf8( url.toEncoded() ) ) );
|
2021-07-06 13:01:50 +00:00
|
|
|
|
2015-10-28 19:56:58 +00:00
|
|
|
}
|
2015-10-30 18:01:38 +00:00
|
|
|
else
|
|
|
|
{
|
2016-04-01 13:38:07 +00:00
|
|
|
url.clear();
|
2015-10-30 18:01:38 +00:00
|
|
|
url.setFragment( anchor );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript(
|
|
|
|
QString( "window.location.hash = \"%1\"" ).arg( QString::fromUtf8( url.toEncoded() ) ) );
|
2015-10-30 18:01:38 +00:00
|
|
|
}
|
2015-10-28 19:56:58 +00:00
|
|
|
}
|
|
|
|
|
2022-02-04 13:19:32 +00:00
|
|
|
//the click audio url such as gdau://xxxx ,webview also emit a pageLoaded signal but with the result is false.need future investigation.
|
|
|
|
//the audio link click ,no need to emit pageLoaded signal
|
|
|
|
if(result){
|
|
|
|
emit pageLoaded( this );
|
|
|
|
}
|
2023-03-17 09:42:44 +00:00
|
|
|
if( Utils::Url::hasQueryItem( webview->url(), "regexp" ) )
|
2014-04-22 13:47:02 +00:00
|
|
|
highlightFTSResults();
|
2009-02-08 16:50:18 +00:00
|
|
|
}
|
|
|
|
|
2022-01-20 12:34:14 +00:00
|
|
|
void ArticleView::loadProgress(int ){
|
|
|
|
}
|
|
|
|
|
2009-01-28 20:55:45 +00:00
|
|
|
void ArticleView::handleTitleChanged( QString const & title )
|
|
|
|
{
|
2016-04-01 13:38:07 +00:00
|
|
|
if( !title.isEmpty() ) // Qt 5.x WebKit raise signal titleChanges(QString()) while navigation within page
|
|
|
|
emit titleChanged( this, title );
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::handleUrlChanged( QUrl const & url )
|
|
|
|
{
|
|
|
|
QIcon icon;
|
|
|
|
|
2009-04-10 12:48:40 +00:00
|
|
|
unsigned group = getGroup( url );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2009-04-10 12:48:40 +00:00
|
|
|
if ( group )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
|
|
|
// Find the group's instance corresponding to the fragment value
|
|
|
|
for( unsigned x = 0; x < groups.size(); ++x )
|
2009-04-10 12:48:40 +00:00
|
|
|
if ( groups[ x ].id == group )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
|
|
|
// Found it
|
|
|
|
|
2010-07-05 19:47:22 +00:00
|
|
|
icon = groups[ x ].makeIcon();
|
|
|
|
break;
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emit iconChanged( this, icon );
|
|
|
|
}
|
|
|
|
|
2009-04-10 12:48:40 +00:00
|
|
|
unsigned ArticleView::getGroup( QUrl const & url )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
if ( url.scheme() == "gdlookup" && Utils::Url::hasQueryItem( url, "group" ) )
|
|
|
|
return Utils::Url::queryItemValue( url, "group" ).toUInt();
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2009-04-10 12:48:40 +00:00
|
|
|
return 0;
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2009-05-11 22:25:22 +00:00
|
|
|
QStringList ArticleView::getArticlesList()
|
2021-12-13 14:45:16 +00:00
|
|
|
{
|
2022-01-08 06:51:24 +00:00
|
|
|
return currentActiveDictIds;
|
2009-05-11 22:25:22 +00:00
|
|
|
}
|
|
|
|
|
2011-07-03 12:27:08 +00:00
|
|
|
QString ArticleView::getActiveArticleId()
|
|
|
|
{
|
2022-01-19 12:16:45 +00:00
|
|
|
return activeDictId;
|
|
|
|
}
|
2011-07-03 12:27:08 +00:00
|
|
|
|
2022-01-19 12:16:45 +00:00
|
|
|
void ArticleView::setActiveArticleId(QString const & dictId){
|
|
|
|
this->activeDictId=dictId;
|
2011-07-03 12:27:08 +00:00
|
|
|
}
|
|
|
|
|
2009-05-11 19:14:28 +00:00
|
|
|
QString ArticleView::getCurrentArticle()
|
|
|
|
{
|
2022-01-20 13:59:47 +00:00
|
|
|
QString dictId=getActiveArticleId();
|
|
|
|
return scrollToFromDictionaryId( dictId );
|
2009-05-11 19:14:28 +00:00
|
|
|
}
|
|
|
|
|
2013-06-28 16:00:13 +00:00
|
|
|
void ArticleView::jumpToDictionary( QString const & id, bool force )
|
2011-06-05 11:49:50 +00:00
|
|
|
{
|
2021-06-29 08:59:16 +00:00
|
|
|
QString targetArticle = scrollToFromDictionaryId( id );
|
2011-07-03 12:27:08 +00:00
|
|
|
|
2013-06-28 16:00:13 +00:00
|
|
|
// jump only if neceessary, or when forced
|
|
|
|
if ( force || targetArticle != getCurrentArticle() )
|
2011-07-03 12:27:08 +00:00
|
|
|
{
|
|
|
|
setCurrentArticle( targetArticle, true );
|
|
|
|
}
|
2011-06-05 11:49:50 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 11:47:15 +00:00
|
|
|
bool ArticleView::setCurrentArticle( QString const & id, bool moveToIt )
|
2009-05-11 22:25:22 +00:00
|
|
|
{
|
2021-06-29 08:59:16 +00:00
|
|
|
if ( !isScrollTo( id ) )
|
2022-05-24 11:47:15 +00:00
|
|
|
return false; // Incorrect id
|
2009-05-11 22:25:22 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( !webview->isVisible() )
|
2022-05-24 11:47:15 +00:00
|
|
|
return false; // No action on background page, scrollIntoView there don't work
|
2014-02-09 15:00:48 +00:00
|
|
|
|
2021-10-05 01:23:30 +00:00
|
|
|
if(moveToIt){
|
2022-02-25 14:48:43 +00:00
|
|
|
QString dictId = id.mid( 7 );
|
|
|
|
if( dictId.isEmpty() )
|
2022-05-24 23:57:43 +00:00
|
|
|
return false;
|
2022-02-25 14:48:43 +00:00
|
|
|
QString script = QString( "var elem=document.getElementById('%1'); "
|
|
|
|
"if(elem!=undefined){elem.scrollIntoView(true);} gdMakeArticleActive('%2',true);" )
|
|
|
|
.arg( id, dictId );
|
|
|
|
onJsActiveArticleChanged( id );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript( script );
|
2022-02-25 14:48:43 +00:00
|
|
|
setActiveArticleId( dictId );
|
2021-10-05 01:23:30 +00:00
|
|
|
}
|
2022-05-24 11:47:15 +00:00
|
|
|
return true;
|
2009-05-11 22:25:22 +00:00
|
|
|
}
|
|
|
|
|
2013-01-18 14:37:24 +00:00
|
|
|
void ArticleView::selectCurrentArticle()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript(
|
|
|
|
QString(
|
|
|
|
"gdSelectArticle( '%1' );var elem=document.getElementById('%2'); if(elem!=undefined){elem.scrollIntoView(true);}" )
|
|
|
|
.arg( getActiveArticleId(), getCurrentArticle() ) );
|
2013-01-18 14:37:24 +00:00
|
|
|
}
|
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
void ArticleView::isFramedArticle( QString const & ca, const std::function< void( bool ) > & callback )
|
2009-05-29 19:48:50 +00:00
|
|
|
{
|
2022-03-30 15:08:24 +00:00
|
|
|
if( ca.isEmpty() )
|
|
|
|
callback( false );
|
2009-05-29 19:48:50 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript( QString( "!!document.getElementById('gdexpandframe-%1');" ).arg( ca.mid( 7 ) ),
|
|
|
|
[ callback ]( const QVariant & res ) {
|
|
|
|
callback( res.toBool() );
|
|
|
|
} );
|
2009-05-29 19:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ArticleView::isExternalLink( QUrl const & url )
|
|
|
|
{
|
2022-01-11 14:20:38 +00:00
|
|
|
return Utils::isExternalLink(url);
|
2009-05-29 19:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::tryMangleWebsiteClickedUrl( QUrl & url, Contexts & contexts )
|
|
|
|
{
|
2010-05-29 11:33:04 +00:00
|
|
|
// Don't try mangling audio urls, even if they are from the framed websites
|
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
if( ( url.scheme() == "http" || url.scheme() == "https" ) && !Dictionary::WebMultimediaDownload::isAudioUrl( url ) )
|
2009-05-29 19:48:50 +00:00
|
|
|
{
|
|
|
|
// Maybe a link inside a website was clicked?
|
|
|
|
|
|
|
|
QString ca = getCurrentArticle();
|
2022-07-08 14:13:40 +00:00
|
|
|
isFramedArticle( ca, []( bool framed ){} );
|
2009-05-29 19:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::load( QUrl const & url ) { webview->load( url ); }
|
2022-06-12 11:26:15 +00:00
|
|
|
|
2009-02-08 21:54:19 +00:00
|
|
|
void ArticleView::cleanupTemp()
|
|
|
|
{
|
2017-03-13 14:38:27 +00:00
|
|
|
QSet< QString >::iterator it = desktopOpenedTempFiles.begin();
|
|
|
|
while( it != desktopOpenedTempFiles.end() )
|
2009-02-08 21:54:19 +00:00
|
|
|
{
|
2017-03-13 14:38:27 +00:00
|
|
|
if( QFile::remove( *it ) )
|
|
|
|
it = desktopOpenedTempFiles.erase( it );
|
|
|
|
else
|
|
|
|
++it;
|
2010-09-16 18:53:39 +00:00
|
|
|
}
|
2009-02-08 21:54:19 +00:00
|
|
|
}
|
|
|
|
|
2013-01-23 18:36:45 +00:00
|
|
|
bool ArticleView::handleF3( QObject * /*obj*/, QEvent * ev )
|
2009-05-12 13:25:18 +00:00
|
|
|
{
|
2014-04-23 13:46:48 +00:00
|
|
|
if ( ev->type() == QEvent::ShortcutOverride
|
|
|
|
|| ev->type() == QEvent::KeyPress )
|
|
|
|
{
|
2013-01-23 18:36:45 +00:00
|
|
|
QKeyEvent * ke = static_cast<QKeyEvent *>( ev );
|
|
|
|
if ( ke->key() == Qt::Key_F3 && isSearchOpened() ) {
|
|
|
|
if ( !ke->modifiers() )
|
|
|
|
{
|
2014-04-23 13:46:48 +00:00
|
|
|
if( ev->type() == QEvent::KeyPress )
|
|
|
|
on_searchNext_clicked();
|
|
|
|
|
2013-01-23 18:36:45 +00:00
|
|
|
ev->accept();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ke->modifiers() == Qt::ShiftModifier )
|
|
|
|
{
|
2014-04-23 13:46:48 +00:00
|
|
|
if( ev->type() == QEvent::KeyPress )
|
|
|
|
on_searchPrevious_clicked();
|
|
|
|
|
2013-01-23 18:36:45 +00:00
|
|
|
ev->accept();
|
|
|
|
return true;
|
|
|
|
}
|
2013-01-22 21:04:45 +00:00
|
|
|
}
|
2014-04-22 13:47:02 +00:00
|
|
|
if ( ke->key() == Qt::Key_F3 && ftsSearchIsOpened )
|
|
|
|
{
|
|
|
|
if ( !ke->modifiers() )
|
|
|
|
{
|
2014-04-23 13:46:48 +00:00
|
|
|
if( ev->type() == QEvent::KeyPress )
|
|
|
|
on_ftsSearchNext_clicked();
|
|
|
|
|
2014-04-22 13:47:02 +00:00
|
|
|
ev->accept();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ke->modifiers() == Qt::ShiftModifier )
|
|
|
|
{
|
2014-04-23 13:46:48 +00:00
|
|
|
if( ev->type() == QEvent::KeyPress )
|
|
|
|
on_ftsSearchPrevious_clicked();
|
|
|
|
|
2013-01-23 18:36:45 +00:00
|
|
|
ev->accept();
|
|
|
|
return true;
|
|
|
|
}
|
2013-01-22 21:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-23 18:36:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ArticleView::eventFilter( QObject * obj, QEvent * ev )
|
|
|
|
{
|
2022-03-27 15:05:45 +00:00
|
|
|
#ifdef Q_OS_MAC
|
2022-03-27 14:22:42 +00:00
|
|
|
|
2022-03-30 15:10:37 +00:00
|
|
|
if( ev->type() == QEvent::NativeGesture )
|
|
|
|
{
|
|
|
|
qDebug() << "it's a Native Gesture!";
|
|
|
|
// handle Qt::ZoomNativeGesture Qt::SmartZoomNativeGesture here
|
|
|
|
// ignore swipe left/right.
|
|
|
|
// QWebEngine can handle Qt::SmartZoomNativeGesture.
|
|
|
|
}
|
2022-03-27 15:05:45 +00:00
|
|
|
|
|
|
|
#else
|
2014-02-04 13:35:42 +00:00
|
|
|
if( ev->type() == QEvent::Gesture )
|
|
|
|
{
|
|
|
|
Gestures::GestureResult result;
|
|
|
|
QPoint pt;
|
|
|
|
|
|
|
|
bool handled = Gestures::handleGestureEvent( obj, ev, result, pt );
|
|
|
|
|
|
|
|
if( handled )
|
|
|
|
{
|
|
|
|
if( result == Gestures::ZOOM_IN )
|
|
|
|
zoomIn();
|
|
|
|
else
|
|
|
|
if( result == Gestures::ZOOM_OUT )
|
|
|
|
zoomOut();
|
|
|
|
else
|
|
|
|
if( result == Gestures::SWIPE_LEFT )
|
|
|
|
back();
|
|
|
|
else
|
|
|
|
if( result == Gestures::SWIPE_RIGHT )
|
|
|
|
forward();
|
|
|
|
else
|
|
|
|
if( result == Gestures::SWIPE_UP || result == Gestures::SWIPE_DOWN )
|
|
|
|
{
|
|
|
|
int delta = result == Gestures::SWIPE_UP ? -120 : 120;
|
|
|
|
QWidget *widget = static_cast< QWidget * >( obj );
|
|
|
|
|
2022-01-08 13:16:22 +00:00
|
|
|
QPoint angleDelta(0, delta);
|
|
|
|
QPoint pixelDetal;
|
2014-02-04 13:35:42 +00:00
|
|
|
QWidget *child = widget->childAt( widget->mapFromGlobal( pt ) );
|
|
|
|
if( child )
|
|
|
|
{
|
2022-01-08 13:16:22 +00:00
|
|
|
QWheelEvent whev( child->mapFromGlobal( pt ), pt, pixelDetal,angleDelta, Qt::NoButton, Qt::NoModifier,Qt::NoScrollPhase,false);
|
2014-02-04 13:35:42 +00:00
|
|
|
qApp->sendEvent( child, &whev );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-08 13:16:22 +00:00
|
|
|
QWheelEvent whev( widget->mapFromGlobal( pt ), pt,pixelDetal, angleDelta,Qt::NoButton, Qt::NoModifier,Qt::NoScrollPhase,false );
|
2014-02-04 13:35:42 +00:00
|
|
|
qApp->sendEvent( widget, &whev );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
}
|
2022-03-27 15:05:45 +00:00
|
|
|
#endif
|
2014-02-04 13:35:42 +00:00
|
|
|
|
|
|
|
if( ev->type() == QEvent::MouseMove )
|
|
|
|
{
|
|
|
|
if( Gestures::isFewTouchPointsPresented() )
|
|
|
|
{
|
|
|
|
ev->accept();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2013-01-23 18:36:45 +00:00
|
|
|
|
|
|
|
if ( handleF3( obj, ev ) )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( obj == webview ) {
|
|
|
|
if( ev->type() == QEvent::MouseButtonPress ) {
|
2011-06-07 08:26:49 +00:00
|
|
|
QMouseEvent * event = static_cast< QMouseEvent * >( ev );
|
|
|
|
if ( event->button() == Qt::XButton1 ) {
|
|
|
|
back();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if ( event->button() == Qt::XButton2 ) {
|
|
|
|
forward();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2021-12-15 15:43:03 +00:00
|
|
|
if ( ev->type() == QEvent::KeyPress || ev->type ()==QEvent::ShortcutOverride)
|
2009-05-12 13:25:18 +00:00
|
|
|
{
|
|
|
|
QKeyEvent * keyEvent = static_cast< QKeyEvent * >( ev );
|
|
|
|
|
2009-05-14 20:43:32 +00:00
|
|
|
if ( keyEvent->modifiers() &
|
2011-05-08 21:24:41 +00:00
|
|
|
( Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier ) )
|
2009-05-14 20:43:32 +00:00
|
|
|
return false; // A non-typing modifier is pressed
|
|
|
|
|
2022-01-14 14:58:19 +00:00
|
|
|
if ( Utils::ignoreKeyEvent(keyEvent)||
|
2014-05-11 11:52:25 +00:00
|
|
|
keyEvent->key() == Qt::Key_Return ||
|
2022-01-14 14:58:19 +00:00
|
|
|
keyEvent->key() == Qt::Key_Enter )
|
2009-05-12 15:54:37 +00:00
|
|
|
return false; // Those key have other uses than to start typing
|
|
|
|
|
2009-05-12 13:25:18 +00:00
|
|
|
QString text = keyEvent->text();
|
|
|
|
|
|
|
|
if ( text.size() )
|
|
|
|
{
|
|
|
|
emit typingEvent( text );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2022-02-17 16:39:24 +00:00
|
|
|
else if( ev->type() == QEvent::Wheel )
|
|
|
|
{
|
|
|
|
QWheelEvent * pe = static_cast< QWheelEvent * >( ev );
|
|
|
|
if( pe->modifiers().testFlag( Qt::ControlModifier ) )
|
|
|
|
{
|
|
|
|
if( pe->angleDelta().y() > 0 )
|
|
|
|
{
|
|
|
|
zoomIn();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
zoomOut();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-12 13:25:18 +00:00
|
|
|
}
|
|
|
|
else
|
2023-03-17 09:42:44 +00:00
|
|
|
return QWidget::eventFilter( obj, ev );
|
2009-05-12 13:25:18 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-09-23 18:44:38 +00:00
|
|
|
QString ArticleView::getMutedForGroup( unsigned group )
|
|
|
|
{
|
2012-09-26 13:13:47 +00:00
|
|
|
if ( dictionaryBarToggled && dictionaryBarToggled->isChecked() )
|
2009-09-23 18:44:38 +00:00
|
|
|
{
|
|
|
|
// Dictionary bar is active -- mute the muted dictionaries
|
|
|
|
Instances::Group const * groupInstance = groups.findGroup( group );
|
|
|
|
|
2012-09-26 13:13:47 +00:00
|
|
|
// Find muted dictionaries for current group
|
|
|
|
Config::Group const * grp = cfg.getGroup( group );
|
|
|
|
Config::MutedDictionaries const * mutedDictionaries;
|
|
|
|
if( group == Instances::Group::AllGroupId )
|
|
|
|
mutedDictionaries = popupView ? &cfg.popupMutedDictionaries : &cfg.mutedDictionaries;
|
|
|
|
else
|
|
|
|
mutedDictionaries = grp ? ( popupView ? &grp->popupMutedDictionaries : &grp->mutedDictionaries ) : 0;
|
|
|
|
if( !mutedDictionaries )
|
|
|
|
return QString();
|
|
|
|
|
2009-09-23 18:44:38 +00:00
|
|
|
QStringList mutedDicts;
|
|
|
|
|
|
|
|
if ( groupInstance )
|
|
|
|
{
|
|
|
|
for( unsigned x = 0; x < groupInstance->dictionaries.size(); ++x )
|
|
|
|
{
|
|
|
|
QString id = QString::fromStdString(
|
|
|
|
groupInstance->dictionaries[ x ]->getId() );
|
|
|
|
|
2010-03-30 20:15:55 +00:00
|
|
|
if ( mutedDictionaries->contains( id ) )
|
2009-09-23 18:44:38 +00:00
|
|
|
mutedDicts.append( id );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( mutedDicts.size() )
|
|
|
|
return mutedDicts.join( "," );
|
|
|
|
}
|
|
|
|
|
|
|
|
return QString();
|
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2022-01-01 10:19:11 +00:00
|
|
|
QStringList ArticleView::getMutedDictionaries(unsigned group) {
|
|
|
|
if (dictionaryBarToggled && dictionaryBarToggled->isChecked()) {
|
|
|
|
// Dictionary bar is active -- mute the muted dictionaries
|
|
|
|
Instances::Group const *groupInstance = groups.findGroup(group);
|
|
|
|
|
|
|
|
// Find muted dictionaries for current group
|
|
|
|
Config::Group const *grp = cfg.getGroup(group);
|
|
|
|
Config::MutedDictionaries const *mutedDictionaries;
|
|
|
|
if (group == Instances::Group::AllGroupId)
|
|
|
|
mutedDictionaries = popupView ? &cfg.popupMutedDictionaries : &cfg.mutedDictionaries;
|
|
|
|
else
|
|
|
|
mutedDictionaries = grp ? (popupView ? &grp->popupMutedDictionaries : &grp->mutedDictionaries) : 0;
|
|
|
|
if (!mutedDictionaries)
|
|
|
|
return QStringList();
|
|
|
|
|
|
|
|
QStringList mutedDicts;
|
|
|
|
|
|
|
|
if (groupInstance) {
|
|
|
|
for (unsigned x = 0; x < groupInstance->dictionaries.size(); ++x) {
|
|
|
|
QString id = QString::fromStdString(groupInstance->dictionaries[x]->getId());
|
|
|
|
|
|
|
|
if (mutedDictionaries->contains(id))
|
|
|
|
mutedDicts.append(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mutedDicts;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QStringList();
|
2021-10-03 11:28:26 +00:00
|
|
|
}
|
|
|
|
|
2021-07-06 13:01:50 +00:00
|
|
|
void ArticleView::linkHovered ( const QString & link )
|
2011-07-02 13:04:49 +00:00
|
|
|
{
|
|
|
|
QString msg;
|
2013-05-31 04:20:25 +00:00
|
|
|
QUrl url(link);
|
2011-07-02 13:04:49 +00:00
|
|
|
|
|
|
|
if ( url.scheme() == "bres" )
|
|
|
|
{
|
|
|
|
msg = tr( "Resource" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( url.scheme() == "gdau" || Dictionary::WebMultimediaDownload::isAudioUrl( url ) )
|
|
|
|
{
|
|
|
|
msg = tr( "Audio" );
|
|
|
|
}
|
|
|
|
else
|
2013-04-24 16:01:44 +00:00
|
|
|
if ( url.scheme() == "gdtts" )
|
|
|
|
{
|
|
|
|
msg = tr( "TTS Voice" );
|
|
|
|
}
|
|
|
|
else
|
2012-12-07 11:59:29 +00:00
|
|
|
if ( url.scheme() == "gdpicture" )
|
|
|
|
{
|
|
|
|
msg = tr( "Picture" );
|
|
|
|
}
|
|
|
|
else
|
2013-06-22 16:36:25 +00:00
|
|
|
if ( url.scheme() == "gdvideo" )
|
|
|
|
{
|
|
|
|
if ( url.path().isEmpty() )
|
|
|
|
{
|
|
|
|
msg = tr( "Video" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QString path = url.path();
|
|
|
|
if ( path.startsWith( '/' ) )
|
|
|
|
{
|
|
|
|
path = path.mid( 1 );
|
|
|
|
}
|
|
|
|
msg = tr( "Video: %1" ).arg( path );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2011-07-02 13:04:49 +00:00
|
|
|
if (url.scheme() == "gdlookup" || url.scheme().compare( "bword" ) == 0)
|
|
|
|
{
|
|
|
|
QString def = url.path();
|
|
|
|
if (def.startsWith("/"))
|
|
|
|
{
|
|
|
|
def = def.mid( 1 );
|
|
|
|
}
|
2012-11-26 13:13:56 +00:00
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
if( Utils::Url::hasQueryItem( url, "dict" ) )
|
2012-11-26 13:13:56 +00:00
|
|
|
{
|
|
|
|
// Link to other dictionary
|
2021-11-27 07:17:33 +00:00
|
|
|
QString dictName( Utils::Url::queryItemValue( url, "dict" ) );
|
2012-11-26 13:13:56 +00:00
|
|
|
if( !dictName.isEmpty() )
|
2022-01-19 23:29:02 +00:00
|
|
|
msg = tr( "Definition from dictionary \"%1\": %2" ).arg( dictName , def );
|
2012-11-26 13:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( msg.isEmpty() )
|
2018-02-21 10:48:11 +00:00
|
|
|
{
|
|
|
|
if( def.isEmpty() && url.hasFragment() )
|
|
|
|
msg = '#' + url.fragment(); // this must be a citation, footnote or backlink
|
|
|
|
else
|
|
|
|
msg = tr( "Definition: %1").arg( def );
|
|
|
|
}
|
2011-07-02 13:04:49 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg = link;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit statusBarMessage( msg );
|
|
|
|
}
|
|
|
|
|
2022-01-19 23:29:02 +00:00
|
|
|
void ArticleView::attachWebChannelToHtml() {
|
2021-12-19 10:37:27 +00:00
|
|
|
// set the web channel to be used by the page
|
|
|
|
// see http://doc.qt.io/qt-5/qwebenginepage.html#setWebChannel
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->setWebChannel(channel, QWebEngineScript::MainWorld);
|
2021-07-06 13:01:50 +00:00
|
|
|
|
2021-12-19 10:37:27 +00:00
|
|
|
// register QObjects to be exposed to JavaScript
|
2022-01-19 23:29:02 +00:00
|
|
|
channel->registerObject(QStringLiteral("articleview"), agent);
|
2011-07-03 12:27:08 +00:00
|
|
|
}
|
2011-07-02 13:04:49 +00:00
|
|
|
|
2009-05-29 19:48:50 +00:00
|
|
|
void ArticleView::linkClicked( QUrl const & url_ )
|
2009-02-08 15:49:17 +00:00
|
|
|
{
|
2017-03-14 14:47:38 +00:00
|
|
|
Qt::KeyboardModifiers kmod = QApplication::keyboardModifiers();
|
|
|
|
|
|
|
|
// Lock jump on links while Alt key is pressed
|
|
|
|
if( kmod & Qt::AltModifier )
|
|
|
|
return;
|
|
|
|
|
2013-05-31 04:20:25 +00:00
|
|
|
QUrl url( url_ );
|
2009-05-29 19:48:50 +00:00
|
|
|
Contexts contexts;
|
|
|
|
|
|
|
|
tryMangleWebsiteClickedUrl( url, contexts );
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( !popupView && ( webview->isMidButtonPressed() || ( kmod & ( Qt::ControlModifier | Qt::ShiftModifier ) ) )
|
|
|
|
&& !isAudioLink( url ) ) {
|
2011-07-06 06:53:42 +00:00
|
|
|
// Mid button or Control/Shift is currently pressed - open the link in new tab
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->resetMidButtonPressed();
|
|
|
|
emit openLinkInNewTab( url, webview->url(), getCurrentArticle(), contexts );
|
2009-10-22 11:38:11 +00:00
|
|
|
}
|
|
|
|
else
|
2023-03-17 09:42:44 +00:00
|
|
|
openLink( url, webview->url(), getCurrentArticle(), contexts );
|
2009-02-08 15:49:17 +00:00
|
|
|
}
|
|
|
|
|
2022-01-14 00:19:49 +00:00
|
|
|
void ArticleView::linkClickedInHtml( QUrl const & url_ )
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
emit webview->linkClickedInHtml( url_ );
|
|
|
|
if( !url_.isEmpty() ) {
|
2022-03-16 14:29:04 +00:00
|
|
|
linkClicked( url_ );
|
|
|
|
}
|
2022-01-14 00:19:49 +00:00
|
|
|
}
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const & scrollTo, Contexts const & contexts_ )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2022-02-04 13:19:32 +00:00
|
|
|
audioPlayer->stop();
|
2021-12-29 15:28:26 +00:00
|
|
|
qDebug() << "open link url:" << url;
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2022-06-07 00:05:49 +00:00
|
|
|
auto [valid, word] = Utils::Url::getQueryWord( url );
|
|
|
|
if( valid && word.isEmpty() )
|
2022-05-27 15:10:50 +00:00
|
|
|
{
|
|
|
|
// invalid gdlookup url.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-28 19:56:58 +00:00
|
|
|
Contexts contexts( contexts_ );
|
|
|
|
|
2012-12-07 11:59:29 +00:00
|
|
|
if( url.scheme().compare( "gdpicture" ) == 0 )
|
2022-06-12 11:26:15 +00:00
|
|
|
load( url );
|
2012-12-07 11:59:29 +00:00
|
|
|
else
|
2022-06-07 00:11:10 +00:00
|
|
|
if ( url.scheme().compare( "bword" ) == 0 || url.scheme().compare( "entry" ) == 0 )
|
2009-08-31 14:27:19 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
if( Utils::Url::hasQueryItem( ref, "dictionaries" ) )
|
2014-04-21 16:20:24 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
QStringList dictsList = Utils::Url::queryItemValue( ref, "dictionaries" )
|
2021-12-29 14:29:06 +00:00
|
|
|
.split( ",", Qt::SkipEmptyParts );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2022-05-27 15:10:50 +00:00
|
|
|
showDefinition( word, dictsList, QRegExp(), getGroup( ref ), false );
|
2014-04-21 16:20:24 +00:00
|
|
|
}
|
|
|
|
else
|
2022-05-27 15:10:50 +00:00
|
|
|
showDefinition( word,
|
2014-04-21 16:20:24 +00:00
|
|
|
getGroup( ref ), scrollTo, contexts );
|
2009-08-31 14:27:19 +00:00
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
else
|
|
|
|
if ( url.scheme() == "gdlookup" ) // Plain html links inherit gdlookup scheme
|
2009-03-26 19:00:08 +00:00
|
|
|
{
|
|
|
|
if ( url.hasFragment() )
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript(
|
2009-03-26 19:00:08 +00:00
|
|
|
QString( "window.location = \"%1\"" ).arg( QString::fromUtf8( url.toEncoded() ) ) );
|
|
|
|
}
|
|
|
|
else
|
2012-11-12 13:52:54 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
if( Utils::Url::hasQueryItem( ref, "dictionaries" ) )
|
2014-04-21 16:20:24 +00:00
|
|
|
{
|
|
|
|
// Specific dictionary group from full-text search
|
2021-11-27 07:17:33 +00:00
|
|
|
QStringList dictsList = Utils::Url::queryItemValue( ref, "dictionaries" )
|
2021-12-29 14:29:06 +00:00
|
|
|
.split( ",", Qt::SkipEmptyParts );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2018-04-10 14:49:52 +00:00
|
|
|
showDefinition( url.path().mid( 1 ), dictsList, QRegExp(), getGroup( ref ), false );
|
2014-04-21 16:20:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-24 20:30:32 +00:00
|
|
|
QString newScrollTo( scrollTo );
|
2021-11-27 07:17:33 +00:00
|
|
|
if( Utils::Url::hasQueryItem( url, "dict" ) )
|
2012-11-24 20:30:32 +00:00
|
|
|
{
|
|
|
|
// Link to other dictionary
|
2021-11-27 07:17:33 +00:00
|
|
|
QString dictName( Utils::Url::queryItemValue( url, "dict" ) );
|
2012-11-24 20:30:32 +00:00
|
|
|
for( unsigned i = 0; i < allDictionaries.size(); i++ )
|
|
|
|
{
|
|
|
|
if( dictName.compare( QString::fromUtf8( allDictionaries[ i ]->getName().c_str() ) ) == 0 )
|
|
|
|
{
|
2021-06-29 08:59:16 +00:00
|
|
|
newScrollTo = scrollToFromDictionaryId( QString::fromUtf8( allDictionaries[ i ]->getId().c_str() ) );
|
2012-11-24 20:30:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-28 19:56:58 +00:00
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
if( Utils::Url::hasQueryItem( url, "gdanchor" ) )
|
|
|
|
contexts[ "gdanchor" ] = Utils::Url::queryItemValue( url, "gdanchor" );
|
2015-10-28 19:56:58 +00:00
|
|
|
|
2021-12-15 15:43:03 +00:00
|
|
|
showDefinition( word,
|
2012-11-24 20:30:32 +00:00
|
|
|
getGroup( ref ), newScrollTo, contexts );
|
2012-11-12 13:52:54 +00:00
|
|
|
}
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
else
|
2013-06-22 16:36:25 +00:00
|
|
|
if ( url.scheme() == "bres" || url.scheme() == "gdau" || url.scheme() == "gdvideo" ||
|
2010-05-29 11:33:04 +00:00
|
|
|
Dictionary::WebMultimediaDownload::isAudioUrl( url ) )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2009-02-06 16:19:05 +00:00
|
|
|
// Download it
|
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
// Clear any pending ones
|
|
|
|
|
|
|
|
resourceDownloadRequests.clear();
|
2009-02-06 16:19:05 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
resourceDownloadUrl = url;
|
2009-02-06 16:19:05 +00:00
|
|
|
|
2010-05-29 11:33:04 +00:00
|
|
|
if ( Dictionary::WebMultimediaDownload::isAudioUrl( url ) )
|
|
|
|
{
|
|
|
|
sptr< Dictionary::DataRequest > req =
|
2022-11-29 03:54:31 +00:00
|
|
|
std::make_shared<Dictionary::WebMultimediaDownload>( url, articleNetMgr );
|
2010-05-29 11:33:04 +00:00
|
|
|
|
|
|
|
resourceDownloadRequests.push_back( req );
|
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
|
2010-05-29 11:33:04 +00:00
|
|
|
}
|
|
|
|
else
|
2009-04-25 21:04:49 +00:00
|
|
|
if ( url.scheme() == "gdau" && url.host() == "search" )
|
2009-02-06 16:19:05 +00:00
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
|
2009-04-10 12:48:40 +00:00
|
|
|
unsigned currentGroup = getGroup( ref );
|
2009-02-06 16:19:05 +00:00
|
|
|
|
2009-04-25 21:04:49 +00:00
|
|
|
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 )
|
2017-04-27 20:55:53 +00:00
|
|
|
{
|
2017-07-09 17:15:35 +00:00
|
|
|
unsigned preferred = UINT_MAX;
|
|
|
|
if( url.hasFragment() )
|
|
|
|
{
|
|
|
|
// Find sound in the preferred dictionary
|
2021-11-27 07:17:33 +00:00
|
|
|
QString preferredName = Utils::Url::fragment( url );
|
2017-07-09 17:15:35 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
for( unsigned x = 0; x < activeDicts->size(); ++x )
|
|
|
|
{
|
|
|
|
if( preferredName.compare( QString::fromUtf8( (*activeDicts)[ x ]->getName().c_str() ) ) == 0 )
|
|
|
|
{
|
|
|
|
preferred = x;
|
|
|
|
sptr< Dictionary::DataRequest > req =
|
|
|
|
(*activeDicts)[ x ]->getResource(
|
|
|
|
url.path().mid( 1 ).toUtf8().data() );
|
|
|
|
|
|
|
|
resourceDownloadRequests.push_back( req );
|
|
|
|
|
|
|
|
if ( !req->isFinished() )
|
|
|
|
{
|
|
|
|
// Queued loading
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
|
2017-07-09 17:15:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Immediate loading
|
|
|
|
if( req->dataSize() > 0 )
|
|
|
|
{
|
|
|
|
// Resource already found, stop next search
|
|
|
|
resourceDownloadFinished();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( std::exception & e )
|
|
|
|
{
|
|
|
|
emit statusBarMessage(
|
2022-01-27 00:23:57 +00:00
|
|
|
tr("ERROR: %1").arg(e.what()),
|
|
|
|
10000, QPixmap(":/icons/error.svg"));
|
2017-07-09 17:15:35 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-25 21:04:49 +00:00
|
|
|
for( unsigned x = 0; x < activeDicts->size(); ++x )
|
2009-02-06 16:19:05 +00:00
|
|
|
{
|
2013-10-18 12:50:29 +00:00
|
|
|
try
|
2009-02-06 16:19:05 +00:00
|
|
|
{
|
2017-07-09 17:15:35 +00:00
|
|
|
if( x == preferred )
|
|
|
|
continue;
|
|
|
|
|
2013-10-18 12:50:29 +00:00
|
|
|
sptr< Dictionary::DataRequest > req =
|
|
|
|
(*activeDicts)[ x ]->getResource(
|
|
|
|
url.path().mid( 1 ).toUtf8().data() );
|
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
resourceDownloadRequests.push_back( req );
|
2013-10-18 12:50:29 +00:00
|
|
|
|
|
|
|
if ( !req->isFinished() )
|
|
|
|
{
|
2017-04-28 12:25:45 +00:00
|
|
|
// Queued loading
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
|
2013-10-18 12:50:29 +00:00
|
|
|
}
|
2017-04-28 12:25:45 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Immediate loading
|
|
|
|
if( req->dataSize() > 0 )
|
|
|
|
{
|
|
|
|
// Resource already found, stop next search
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-04-25 21:04:49 +00:00
|
|
|
}
|
2013-10-18 12:50:29 +00:00
|
|
|
catch( std::exception & e )
|
2009-04-25 21:04:49 +00:00
|
|
|
{
|
2013-10-18 12:50:29 +00:00
|
|
|
emit statusBarMessage(
|
2022-01-27 00:23:57 +00:00
|
|
|
tr("ERROR: %1").arg(e.what()),
|
|
|
|
10000, QPixmap(":/icons/error.svg"));
|
2009-02-06 16:19:05 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-27 20:55:53 +00:00
|
|
|
}
|
2009-02-06 16:19:05 +00:00
|
|
|
}
|
2009-03-26 19:00:08 +00:00
|
|
|
else
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2009-03-26 19:00:08 +00:00
|
|
|
// Normal resource download
|
|
|
|
QString contentType;
|
2009-02-08 14:40:26 +00:00
|
|
|
|
2010-09-16 18:53:39 +00:00
|
|
|
sptr< Dictionary::DataRequest > req =
|
2009-03-26 19:00:08 +00:00
|
|
|
articleNetMgr.getResource( url, contentType );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2009-04-25 21:04:49 +00:00
|
|
|
if ( !req.get() )
|
|
|
|
{
|
|
|
|
// Request failed, fail
|
|
|
|
}
|
|
|
|
else
|
2009-03-26 19:00:08 +00:00
|
|
|
if ( req->isFinished() && req->dataSize() >= 0 )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2009-03-26 19:00:08 +00:00
|
|
|
// Have data ready, handle it
|
|
|
|
resourceDownloadRequests.push_back( req );
|
|
|
|
resourceDownloadFinished();
|
2009-02-08 14:40:26 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
return;
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
2009-03-26 19:00:08 +00:00
|
|
|
else
|
|
|
|
if ( !req->isFinished() )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2009-03-26 19:00:08 +00:00
|
|
|
// Queue to be handled when done
|
2009-02-08 14:40:26 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
resourceDownloadRequests.push_back( req );
|
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( req.get(), &Dictionary::Request::finished, this, &ArticleView::resourceDownloadFinished );
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
2009-02-08 14:40:26 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
if ( resourceDownloadRequests.empty() ) // No requests were queued
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2013-02-01 12:36:01 +00:00
|
|
|
QMessageBox::critical( this, "GoldenDict", tr( "The referenced resource doesn't exist." ) );
|
2009-03-26 19:00:08 +00:00
|
|
|
return;
|
2009-02-08 21:54:19 +00:00
|
|
|
}
|
2009-04-25 21:04:49 +00:00
|
|
|
else
|
|
|
|
resourceDownloadFinished(); // Check any requests finished already
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
2009-02-01 00:08:08 +00:00
|
|
|
else
|
2011-05-29 05:08:37 +00:00
|
|
|
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.
|
2011-06-04 21:35:09 +00:00
|
|
|
Programs::RunInstance * req = new Programs::RunInstance;
|
2011-05-29 05:08:37 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( req, &Programs::RunInstance::finished, req, &QObject::deleteLater );
|
2011-05-29 05:08:37 +00:00
|
|
|
|
2011-06-04 21:35:09 +00:00
|
|
|
QString error;
|
|
|
|
|
|
|
|
// Delete the request if it fails to start
|
|
|
|
if ( !req->start( *i, url.path().mid( 1 ), error ) )
|
|
|
|
{
|
2011-05-29 05:08:37 +00:00
|
|
|
delete req;
|
|
|
|
|
2013-02-01 12:36:01 +00:00
|
|
|
QMessageBox::critical( this, "GoldenDict",
|
2011-06-04 21:35:09 +00:00
|
|
|
error );
|
|
|
|
}
|
|
|
|
|
2011-05-29 05:08:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Still here? No such program exists.
|
2013-02-01 12:36:01 +00:00
|
|
|
QMessageBox::critical( this, "GoldenDict",
|
2011-05-29 05:08:37 +00:00
|
|
|
tr( "The referenced audio program doesn't exist." ) );
|
|
|
|
}
|
|
|
|
else
|
2013-04-24 14:52:04 +00:00
|
|
|
if ( url.scheme() == "gdtts" )
|
|
|
|
{
|
|
|
|
// TODO: Port TTS
|
2014-05-12 13:53:13 +00:00
|
|
|
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
2013-04-24 14:52:04 +00:00
|
|
|
// Text to speech
|
2021-11-27 07:17:33 +00:00
|
|
|
QString md5Id = Utils::Url::queryItemValue( url, "engine" );
|
2013-04-24 14:52:04 +00:00
|
|
|
QString text( url.path().mid( 1 ) );
|
|
|
|
|
|
|
|
for ( Config::VoiceEngines::const_iterator i = cfg.voiceEngines.begin();
|
|
|
|
i != cfg.voiceEngines.end(); ++i )
|
|
|
|
{
|
|
|
|
QString itemMd5Id = QString( QCryptographicHash::hash(
|
|
|
|
i->id.toUtf8(),
|
2013-04-24 16:01:44 +00:00
|
|
|
QCryptographicHash::Md5 ).toHex() );
|
2013-04-24 14:52:04 +00:00
|
|
|
|
2013-04-24 16:01:44 +00:00
|
|
|
if ( itemMd5Id == md5Id )
|
|
|
|
{
|
2013-04-26 13:41:39 +00:00
|
|
|
SpeechClient * speechClient = new SpeechClient( *i, this );
|
2013-04-24 14:52:04 +00:00
|
|
|
connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) );
|
2013-04-24 16:01:44 +00:00
|
|
|
speechClient->tell( text );
|
2013-04-24 14:52:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
2010-06-30 16:43:08 +00:00
|
|
|
if ( isExternalLink( url ) )
|
2009-02-01 00:08:08 +00:00
|
|
|
{
|
2010-06-30 16:43:08 +00:00
|
|
|
// Use the system handler for the conventional external links
|
2009-02-01 00:08:08 +00:00
|
|
|
QDesktopServices::openUrl( url );
|
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
ResourceToSaveHandler * ArticleView::saveResource( const QUrl & url, const QString & fileName )
|
2013-02-22 12:44:23 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
return saveResource( url, webview->url(), fileName );
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
ResourceToSaveHandler * ArticleView::saveResource( const QUrl & url, const QUrl & ref, const QString & fileName )
|
2013-05-27 11:18:13 +00:00
|
|
|
{
|
2017-04-27 20:55:53 +00:00
|
|
|
ResourceToSaveHandler * handler = new ResourceToSaveHandler( this, fileName );
|
2013-02-22 12:44:23 +00:00
|
|
|
sptr< Dictionary::DataRequest > req;
|
|
|
|
|
2017-04-27 15:05:28 +00:00
|
|
|
if( url.scheme() == "bres" || url.scheme() == "gico" || url.scheme() == "gdau" || url.scheme() == "gdvideo" )
|
2013-02-22 12:44:23 +00:00
|
|
|
{
|
|
|
|
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 )
|
|
|
|
{
|
2019-06-27 15:09:00 +00:00
|
|
|
unsigned preferred = UINT_MAX;
|
|
|
|
if( url.hasFragment() && url.scheme() == "gdau" )
|
|
|
|
{
|
|
|
|
// Find sound in the preferred dictionary
|
2021-11-27 07:17:33 +00:00
|
|
|
QString preferredName = Utils::Url::fragment( url );
|
2019-06-27 15:09:00 +00:00
|
|
|
for( unsigned x = 0; x < activeDicts->size(); ++x )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if( preferredName.compare( QString::fromUtf8( (*activeDicts)[ x ]->getName().c_str() ) ) == 0 )
|
|
|
|
{
|
|
|
|
preferred = x;
|
|
|
|
sptr< Dictionary::DataRequest > req =
|
|
|
|
(*activeDicts)[ x ]->getResource(
|
|
|
|
url.path().mid( 1 ).toUtf8().data() );
|
|
|
|
|
|
|
|
handler->addRequest( req );
|
|
|
|
|
|
|
|
if( req->isFinished() && req->dataSize() > 0 )
|
|
|
|
{
|
|
|
|
handler->downloadFinished();
|
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( std::exception & e )
|
|
|
|
{
|
|
|
|
gdWarning( "getResource request error (%s) in \"%s\"\n", e.what(),
|
|
|
|
(*activeDicts)[ x ]->getName().c_str() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-22 12:44:23 +00:00
|
|
|
for( unsigned x = 0; x < activeDicts->size(); ++x )
|
|
|
|
{
|
2013-10-18 12:50:29 +00:00
|
|
|
try
|
|
|
|
{
|
2019-06-27 15:09:00 +00:00
|
|
|
if( x == preferred )
|
|
|
|
continue;
|
|
|
|
|
2013-10-18 12:50:29 +00:00
|
|
|
req = (*activeDicts)[ x ]->getResource(
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::path( url ).mid( 1 ).toUtf8().data() );
|
2013-02-22 12:44:23 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
handler->addRequest( req );
|
2017-04-28 12:25:45 +00:00
|
|
|
|
|
|
|
if( req->isFinished() && req->dataSize() > 0 )
|
|
|
|
{
|
|
|
|
// Resource already found, stop next search
|
|
|
|
break;
|
|
|
|
}
|
2013-10-18 12:50:29 +00:00
|
|
|
}
|
|
|
|
catch( std::exception & e )
|
|
|
|
{
|
2013-11-16 18:34:09 +00:00
|
|
|
gdWarning( "getResource request error (%s) in \"%s\"\n", e.what(),
|
2013-10-18 12:50:29 +00:00
|
|
|
(*activeDicts)[ x ]->getName().c_str() );
|
|
|
|
}
|
2013-02-22 12:44:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Normal resource download
|
|
|
|
QString contentType;
|
|
|
|
req = articleNetMgr.getResource( url, contentType );
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2013-10-18 14:32:58 +00:00
|
|
|
if( req.get() )
|
|
|
|
{
|
2017-04-27 20:55:53 +00:00
|
|
|
handler->addRequest( req );
|
2013-10-18 14:32:58 +00:00
|
|
|
}
|
2013-02-22 12:44:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-11-29 03:54:31 +00:00
|
|
|
req = std::make_shared<Dictionary::WebMultimediaDownload>( url, articleNetMgr );
|
2013-02-22 12:44:23 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
handler->addRequest( req );
|
2013-02-22 12:44:23 +00:00
|
|
|
}
|
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
if ( handler->isEmpty() ) // No requests were queued
|
2013-02-22 12:44:23 +00:00
|
|
|
{
|
|
|
|
emit statusBarMessage(
|
2022-01-27 00:23:57 +00:00
|
|
|
tr("ERROR: %1").arg(tr("The referenced resource doesn't exist.")),
|
|
|
|
10000, QPixmap(":/icons/error.svg"));
|
2013-02-22 12:44:23 +00:00
|
|
|
}
|
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
// Check already finished downloads
|
|
|
|
handler->downloadFinished();
|
|
|
|
|
|
|
|
return handler;
|
2013-02-22 12:44:23 +00:00
|
|
|
}
|
|
|
|
|
2009-09-23 18:44:38 +00:00
|
|
|
void ArticleView::updateMutedContents()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
QUrl currentUrl = webview->url();
|
2009-09-23 18:44:38 +00:00
|
|
|
|
|
|
|
if ( currentUrl.scheme() != "gdlookup" )
|
|
|
|
return; // Weird url -- do nothing
|
|
|
|
|
|
|
|
unsigned group = getGroup( currentUrl );
|
|
|
|
|
|
|
|
if ( !group )
|
|
|
|
return; // No group in url -- do nothing
|
|
|
|
|
|
|
|
QString mutedDicts = getMutedForGroup( group );
|
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
if ( Utils::Url::queryItemValue( currentUrl, "muted" ) != mutedDicts )
|
2009-09-23 18:44:38 +00:00
|
|
|
{
|
|
|
|
// The list has changed -- update the url
|
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::removeQueryItem( currentUrl, "muted" );
|
2009-09-23 18:44:38 +00:00
|
|
|
|
|
|
|
if ( mutedDicts.size() )
|
2021-11-27 07:17:33 +00:00
|
|
|
Utils::Url::addQueryItem( currentUrl, "muted", mutedDicts );
|
2009-09-23 18:44:38 +00:00
|
|
|
|
2022-06-12 11:26:15 +00:00
|
|
|
load( currentUrl );
|
2009-09-23 18:44:38 +00:00
|
|
|
|
|
|
|
//QApplication::setOverrideCursor( Qt::WaitCursor );
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->setCursor( Qt::WaitCursor );
|
2009-09-23 18:44:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-07 11:27:19 +00:00
|
|
|
bool ArticleView::canGoBack()
|
|
|
|
{
|
|
|
|
// First entry in a history is always an empty page,
|
|
|
|
// so we skip it.
|
2023-03-17 09:42:44 +00:00
|
|
|
return webview->history()->currentItemIndex() > 1;
|
2011-06-07 11:27:19 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
bool ArticleView::canGoForward() { return webview->history()->canGoForward(); }
|
2011-06-07 11:27:19 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::setSelectionBySingleClick( bool set ) { webview->setSelectionBySingleClick( set ); }
|
2012-09-26 13:59:48 +00:00
|
|
|
|
2022-06-01 15:11:41 +00:00
|
|
|
void ArticleView::setDelayedHighlightText(QString const & text)
|
|
|
|
{
|
|
|
|
delayedHighlightText = text;
|
|
|
|
}
|
|
|
|
|
2011-06-07 07:57:25 +00:00
|
|
|
void ArticleView::back()
|
|
|
|
{
|
|
|
|
// Don't allow navigating back to page 0, which is usually the initial
|
|
|
|
// empty page
|
2011-06-07 11:27:19 +00:00
|
|
|
if ( canGoBack() )
|
2011-06-07 07:57:25 +00:00
|
|
|
{
|
2022-11-04 13:23:42 +00:00
|
|
|
currentActiveDictIds.clear();
|
|
|
|
historyMode = true;
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->back();
|
2011-06-07 07:57:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::forward()
|
|
|
|
{
|
2022-11-04 13:23:42 +00:00
|
|
|
currentActiveDictIds.clear();
|
|
|
|
historyMode = true;
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->forward();
|
2011-06-07 07:57:25 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::reload() { webview->reload(); }
|
2022-06-11 17:57:52 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
void ArticleView::hasSound( const std::function< void( bool ) > & callback )
|
2009-04-10 21:07:03 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript( "if(typeof(gdAudioLinks)!=\"undefined\") gdAudioLinks.first",
|
|
|
|
[ callback ]( const QVariant & v ) {
|
|
|
|
bool has = false;
|
|
|
|
if( v.type() == QVariant::String )
|
|
|
|
has = !v.toString().isEmpty();
|
|
|
|
callback( has );
|
|
|
|
} );
|
2009-04-10 21:07:03 +00:00
|
|
|
}
|
|
|
|
|
2021-12-13 14:45:16 +00:00
|
|
|
//use webengine javascript to playsound
|
2009-04-10 21:07:03 +00:00
|
|
|
void ArticleView::playSound()
|
|
|
|
{
|
2022-01-29 03:32:44 +00:00
|
|
|
QString variable = " (function(){ var link=gdAudioLinks[gdAudioLinks.current]; "
|
2021-11-24 14:38:37 +00:00
|
|
|
" if(link==undefined){ "
|
|
|
|
" link=gdAudioLinks.first; "
|
|
|
|
" } "
|
2022-01-29 03:32:44 +00:00
|
|
|
" return link;})(); ";
|
2022-01-31 00:42:36 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript( variable, [ this ]( const QVariant & result ) {
|
|
|
|
if( result.type() == QVariant::String ) {
|
|
|
|
QString soundScript = result.toString();
|
|
|
|
if( !soundScript.isEmpty() )
|
|
|
|
openLink( QUrl::fromEncoded( soundScript.toUtf8() ), webview->url() );
|
|
|
|
}
|
|
|
|
} );
|
2009-04-10 21:07:03 +00:00
|
|
|
}
|
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
void ArticleView::toHtml( const std::function< void( QString & ) > & callback )
|
2009-05-01 11:17:29 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->toHtml( [ = ]( const QString & content ) {
|
|
|
|
QString html = content;
|
|
|
|
callback( html );
|
|
|
|
} );
|
2009-05-01 11:17:29 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::setHtml( const QString & content, const QUrl & baseUrl )
|
|
|
|
{
|
|
|
|
webview->page()->setHtml( content, baseUrl );
|
2021-08-21 01:41:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
void ArticleView::setContent( const QByteArray & data, const QString & mimeType, const QUrl & baseUrl )
|
2009-05-01 11:17:29 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->setContent( data, mimeType, baseUrl );
|
2009-05-01 11:17:29 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
QString ArticleView::getTitle() { return webview->page()->title(); }
|
|
|
|
|
2021-06-10 16:13:11 +00:00
|
|
|
Config::InputPhrase ArticleView::getPhrase() const
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
const QUrl url = webview->url();
|
2021-11-27 07:17:33 +00:00
|
|
|
return Config::InputPhrase( Utils::Url::queryItemValue( url, "word" ),
|
|
|
|
Utils::Url::queryItemValue( url, "punctuation_suffix" ) );
|
2021-06-10 16:13:11 +00:00
|
|
|
}
|
|
|
|
|
2009-05-01 12:20:33 +00:00
|
|
|
void ArticleView::print( QPrinter * printer ) const
|
|
|
|
{
|
2022-03-12 10:12:17 +00:00
|
|
|
QEventLoop loop;
|
|
|
|
bool result;
|
|
|
|
auto printPreview = [ & ]( bool success )
|
|
|
|
{
|
|
|
|
result = success;
|
|
|
|
loop.quit();
|
|
|
|
};
|
2022-03-29 12:34:41 +00:00
|
|
|
#if( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->print( printer, std::move( printPreview ) );
|
2022-03-29 12:34:41 +00:00
|
|
|
#else
|
2023-03-17 09:42:44 +00:00
|
|
|
connect( webview, &QWebEngineView::printFinished, &loop, std::move( printPreview ) );
|
|
|
|
webview->print( printer );
|
2022-03-29 12:34:41 +00:00
|
|
|
#endif
|
2022-03-12 10:12:17 +00:00
|
|
|
loop.exec();
|
|
|
|
if( !result )
|
|
|
|
{
|
|
|
|
qDebug() << "print failed";
|
|
|
|
}
|
2009-05-01 12:20:33 +00:00
|
|
|
}
|
|
|
|
|
2009-01-28 20:55:45 +00:00
|
|
|
void ArticleView::contextMenuRequested( QPoint const & pos )
|
|
|
|
{
|
|
|
|
// Is that a link? Is there a selection?
|
2023-03-17 09:42:44 +00:00
|
|
|
QWebEnginePage * r = webview->page();
|
2009-01-28 20:55:45 +00:00
|
|
|
QMenu menu( this );
|
|
|
|
|
|
|
|
QAction * followLink = 0;
|
2009-05-29 19:48:50 +00:00
|
|
|
QAction * followLinkExternal = 0;
|
2009-02-08 15:49:17 +00:00
|
|
|
QAction * followLinkNewTab = 0;
|
2009-01-28 20:55:45 +00:00
|
|
|
QAction * lookupSelection = 0;
|
2022-05-21 06:03:26 +00:00
|
|
|
QAction * sendToAnkiAction = 0 ;
|
2009-05-14 19:27:19 +00:00
|
|
|
QAction * lookupSelectionGr = 0;
|
2009-02-08 15:49:17 +00:00
|
|
|
QAction * lookupSelectionNewTab = 0;
|
2009-05-14 19:27:19 +00:00
|
|
|
QAction * lookupSelectionNewTabGr = 0;
|
2011-07-31 00:11:07 +00:00
|
|
|
QAction * maxDictionaryRefsAction = 0;
|
2012-09-12 14:11:30 +00:00
|
|
|
QAction * addWordToHistoryAction = 0;
|
2012-09-15 13:24:04 +00:00
|
|
|
QAction * addHeaderToHistoryAction = 0;
|
2012-11-26 13:13:13 +00:00
|
|
|
QAction * sendWordToInputLineAction = 0;
|
2013-02-22 12:44:23 +00:00
|
|
|
QAction * saveImageAction = 0;
|
2022-02-27 05:17:37 +00:00
|
|
|
QAction * saveSoundAction = 0;
|
2022-06-01 15:11:41 +00:00
|
|
|
QAction * saveBookmark = 0;
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2022-02-27 05:17:37 +00:00
|
|
|
#if( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
|
|
|
|
const QWebEngineContextMenuData * menuData = &(r->contextMenuData());
|
|
|
|
#else
|
2023-03-17 09:42:44 +00:00
|
|
|
QWebEngineContextMenuRequest * menuData = webview->lastContextMenuRequest();
|
2022-02-27 05:17:37 +00:00
|
|
|
#endif
|
|
|
|
QUrl targetUrl(menuData->linkUrl());
|
2009-05-29 19:48:50 +00:00
|
|
|
Contexts contexts;
|
|
|
|
|
|
|
|
tryMangleWebsiteClickedUrl( targetUrl, contexts );
|
|
|
|
|
2021-12-29 12:09:29 +00:00
|
|
|
if ( !targetUrl.isEmpty() )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2009-05-29 19:48:50 +00:00
|
|
|
if ( !isExternalLink( targetUrl ) )
|
2009-02-08 15:49:17 +00:00
|
|
|
{
|
2022-12-27 09:54:03 +00:00
|
|
|
followLink = new QAction( tr( "Op&en Link" ), &menu );
|
2009-05-29 19:48:50 +00:00
|
|
|
menu.addAction( followLink );
|
2009-04-03 21:57:23 +00:00
|
|
|
|
2022-07-10 02:09:58 +00:00
|
|
|
if( !popupView && !isAudioLink( targetUrl ) )
|
2009-05-29 19:48:50 +00:00
|
|
|
{
|
2022-01-24 14:23:38 +00:00
|
|
|
followLinkNewTab = new QAction( QIcon( ":/icons/addtab.svg" ),
|
2009-05-29 19:48:50 +00:00
|
|
|
tr( "Open Link in New &Tab" ), &menu );
|
|
|
|
menu.addAction( followLinkNewTab );
|
|
|
|
}
|
|
|
|
}
|
2009-04-03 21:57:23 +00:00
|
|
|
|
2021-12-29 12:09:29 +00:00
|
|
|
if ( isExternalLink( targetUrl ) )
|
2009-05-29 19:48:50 +00:00
|
|
|
{
|
|
|
|
followLinkExternal = new QAction( tr( "Open Link in &External Browser" ), &menu );
|
|
|
|
menu.addAction( followLinkExternal );
|
2023-03-17 09:42:44 +00:00
|
|
|
menu.addAction( webview->pageAction( QWebEnginePage::CopyLinkToClipboard ) );
|
2009-05-29 19:48:50 +00:00
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2022-01-02 08:30:16 +00:00
|
|
|
QUrl imageUrl;
|
2022-02-27 05:17:37 +00:00
|
|
|
#if( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
|
|
|
|
if( !popupView && menuData->mediaType ()==QWebEngineContextMenuData::MediaTypeImage)
|
|
|
|
#else
|
|
|
|
if( !popupView && menuData->mediaType ()==QWebEngineContextMenuRequest::MediaType::MediaTypeImage)
|
|
|
|
#endif
|
2022-01-02 08:30:16 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
imageUrl = menuData->mediaUrl();
|
|
|
|
if( !imageUrl.isEmpty() ) {
|
|
|
|
menu.addAction( webview->pageAction( QWebEnginePage::CopyImageToClipboard ) );
|
|
|
|
saveImageAction = new QAction( tr( "Save &image..." ), &menu );
|
|
|
|
menu.addAction( saveImageAction );
|
|
|
|
}
|
2022-01-02 08:30:16 +00:00
|
|
|
}
|
|
|
|
|
2022-07-10 02:09:58 +00:00
|
|
|
if( !popupView && isAudioLink( targetUrl ) )
|
2022-01-02 08:30:16 +00:00
|
|
|
{
|
|
|
|
saveSoundAction = new QAction( tr( "Save s&ound..." ), &menu );
|
|
|
|
menu.addAction( saveSoundAction );
|
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
QString selectedText = webview->selectedText();
|
2022-05-29 04:26:39 +00:00
|
|
|
QString text = Utils::trimNonChar( selectedText );
|
2009-09-08 20:31:31 +00:00
|
|
|
|
2017-04-24 14:41:07 +00:00
|
|
|
if ( text.size() && text.size() < 60 )
|
2009-01-28 20:55:45 +00:00
|
|
|
{
|
2009-09-08 20:31:31 +00:00
|
|
|
// We don't prompt for selections larger or equal to 60 chars, since
|
|
|
|
// it ruins the menu and it's hardly a single word anyway.
|
|
|
|
|
2013-07-06 21:45:13 +00:00
|
|
|
if( text.isRightToLeft() )
|
|
|
|
{
|
|
|
|
text.insert( 0, (ushort)0x202E ); // RLE, Right-to-Left Embedding
|
|
|
|
text.append( (ushort)0x202C ); // PDF, POP DIRECTIONAL FORMATTING
|
|
|
|
}
|
|
|
|
|
2009-05-14 19:27:19 +00:00
|
|
|
lookupSelection = new QAction( tr( "&Look up \"%1\"" ).
|
2013-07-06 21:45:13 +00:00
|
|
|
arg( text ),
|
2009-05-14 19:27:19 +00:00
|
|
|
&menu );
|
2009-01-28 20:55:45 +00:00
|
|
|
menu.addAction( lookupSelection );
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2009-02-08 15:49:17 +00:00
|
|
|
if ( !popupView )
|
|
|
|
{
|
2022-01-24 14:23:38 +00:00
|
|
|
lookupSelectionNewTab = new QAction( QIcon( ":/icons/addtab.svg" ),
|
2009-05-14 19:27:19 +00:00
|
|
|
tr( "Look up \"%1\" in &New Tab" ).
|
2013-07-06 21:45:13 +00:00
|
|
|
arg( text ),
|
2009-05-14 19:27:19 +00:00
|
|
|
&menu );
|
2009-02-08 15:49:17 +00:00
|
|
|
menu.addAction( lookupSelectionNewTab );
|
2012-11-26 13:13:13 +00:00
|
|
|
|
|
|
|
sendWordToInputLineAction = new QAction( tr( "Send \"%1\" to input line" ).
|
2013-07-06 21:45:13 +00:00
|
|
|
arg( text ),
|
2012-11-26 13:13:13 +00:00
|
|
|
&menu );
|
|
|
|
menu.addAction( sendWordToInputLineAction );
|
2009-02-08 15:49:17 +00:00
|
|
|
}
|
2009-04-03 21:57:23 +00:00
|
|
|
|
2012-09-16 10:30:14 +00:00
|
|
|
addWordToHistoryAction = new QAction( tr( "&Add \"%1\" to history" ).
|
2013-07-06 21:45:13 +00:00
|
|
|
arg( text ),
|
2012-09-16 10:30:14 +00:00
|
|
|
&menu );
|
|
|
|
menu.addAction( addWordToHistoryAction );
|
|
|
|
|
2009-05-14 19:27:19 +00:00
|
|
|
Instances::Group const * altGroup =
|
2023-03-17 09:42:44 +00:00
|
|
|
( groupComboBox && groupComboBox->getCurrentGroup() != getGroup( webview->url() ) ) ?
|
|
|
|
groups.findGroup( groupComboBox->getCurrentGroup() ) :
|
|
|
|
0;
|
2009-05-14 19:27:19 +00:00
|
|
|
|
|
|
|
if ( altGroup )
|
|
|
|
{
|
|
|
|
QIcon icon = altGroup->icon.size() ? QIcon( ":/flags/" + altGroup->icon ) :
|
|
|
|
QIcon();
|
|
|
|
|
2009-05-24 16:40:53 +00:00
|
|
|
lookupSelectionGr = new QAction( icon, tr( "Look up \"%1\" in %2" ).
|
2017-04-24 14:41:07 +00:00
|
|
|
arg( text ).
|
2009-05-14 19:27:19 +00:00
|
|
|
arg( altGroup->name ), &menu );
|
|
|
|
menu.addAction( lookupSelectionGr );
|
|
|
|
|
|
|
|
if ( !popupView )
|
|
|
|
{
|
2022-01-24 14:23:38 +00:00
|
|
|
lookupSelectionNewTabGr = new QAction( QIcon( ":/icons/addtab.svg" ),
|
2009-05-14 19:27:19 +00:00
|
|
|
tr( "Look up \"%1\" in %2 in &New Tab" ).
|
2017-04-24 14:41:07 +00:00
|
|
|
arg( text ).
|
2009-05-14 19:27:19 +00:00
|
|
|
arg( altGroup->name ), &menu );
|
|
|
|
menu.addAction( lookupSelectionNewTabGr );
|
|
|
|
}
|
|
|
|
}
|
2009-09-08 20:31:31 +00:00
|
|
|
}
|
2009-05-14 19:27:19 +00:00
|
|
|
|
2022-06-01 15:11:41 +00:00
|
|
|
if(text.size())
|
|
|
|
{
|
2022-06-01 15:40:00 +00:00
|
|
|
// avoid too long in the menu ,use left 30 characters.
|
|
|
|
saveBookmark = new QAction( tr( "Save &Bookmark \"%1...\"" ).arg( text.left( 30 ) ), &menu );
|
2022-06-01 15:11:41 +00:00
|
|
|
menu.addAction( saveBookmark );
|
|
|
|
}
|
|
|
|
|
2022-05-21 06:03:26 +00:00
|
|
|
// add anki menu
|
|
|
|
if( !text.isEmpty() && cfg.preferences.ankiConnectServer.enabled )
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
QString txt = webview->title();
|
2022-05-21 06:03:26 +00:00
|
|
|
sendToAnkiAction = new QAction( tr( "&Send \"%1\" to anki with selected text." ).arg( txt ), &menu );
|
|
|
|
menu.addAction( sendToAnkiAction );
|
|
|
|
}
|
|
|
|
|
2017-04-24 14:41:07 +00:00
|
|
|
if( text.isEmpty() && !cfg.preferences.storeHistory)
|
2013-01-17 18:38:49 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
QString txt = webview->title();
|
2015-10-14 15:18:11 +00:00
|
|
|
if( txt.size() > 60 )
|
|
|
|
txt = txt.left( 60 ) + "...";
|
|
|
|
|
|
|
|
addHeaderToHistoryAction = new QAction( tr( "&Add \"%1\" to history" ).
|
|
|
|
arg( txt ),
|
2013-01-17 18:38:49 +00:00
|
|
|
&menu );
|
2015-10-14 15:18:11 +00:00
|
|
|
menu.addAction( addHeaderToHistoryAction );
|
2013-01-17 18:38:49 +00:00
|
|
|
}
|
|
|
|
|
2013-01-18 13:22:24 +00:00
|
|
|
if ( selectedText.size() )
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
menu.addAction( webview->pageAction( QWebEnginePage::Copy ) );
|
2013-05-15 13:52:47 +00:00
|
|
|
menu.addAction( ©AsTextAction );
|
2013-01-18 13:22:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-01-18 14:37:24 +00:00
|
|
|
menu.addAction( &selectCurrentArticleAction );
|
2023-03-17 09:42:44 +00:00
|
|
|
menu.addAction( webview->pageAction( QWebEnginePage::SelectAll ) );
|
2013-01-18 13:22:24 +00:00
|
|
|
}
|
|
|
|
|
2009-04-12 16:22:42 +00:00
|
|
|
map< QAction *, QString > tableOfContents;
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2009-04-12 16:22:42 +00:00
|
|
|
// Add table of contents
|
2009-05-11 22:25:22 +00:00
|
|
|
QStringList ids = getArticlesList();
|
2009-04-12 16:22:42 +00:00
|
|
|
|
|
|
|
if ( !menu.isEmpty() && ids.size() )
|
|
|
|
menu.addSeparator();
|
2009-04-14 18:35:27 +00:00
|
|
|
|
|
|
|
unsigned refsAdded = 0;
|
2011-07-31 00:11:07 +00:00
|
|
|
bool maxDictionaryRefsReached = false;
|
2009-04-14 18:35:27 +00:00
|
|
|
|
2009-04-12 16:22:42 +00:00
|
|
|
for( QStringList::const_iterator i = ids.constBegin(); i != ids.constEnd();
|
2009-04-14 18:35:27 +00:00
|
|
|
++i, ++refsAdded )
|
2009-04-12 16:22:42 +00:00
|
|
|
{
|
|
|
|
// Find this dictionary
|
|
|
|
|
|
|
|
for( unsigned x = allDictionaries.size(); x--; )
|
|
|
|
{
|
|
|
|
if ( allDictionaries[ x ]->getId() == i->toUtf8().data() )
|
|
|
|
{
|
2011-07-31 00:11:07 +00:00
|
|
|
QAction * action = 0;
|
2013-06-11 18:31:01 +00:00
|
|
|
if ( refsAdded == cfg.preferences.maxDictionaryRefsInContextMenu )
|
2011-07-31 00:11:07 +00:00
|
|
|
{
|
|
|
|
// Enough! Or the menu would become too large.
|
|
|
|
maxDictionaryRefsAction = new QAction( ".........", &menu );
|
|
|
|
action = maxDictionaryRefsAction;
|
|
|
|
maxDictionaryRefsReached = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
action = new QAction(
|
|
|
|
allDictionaries[ x ]->getIcon(),
|
|
|
|
QString::fromUtf8( allDictionaries[ x ]->getName().c_str() ),
|
|
|
|
&menu );
|
2018-07-07 09:33:15 +00:00
|
|
|
// Force icons in menu on all platforms,
|
2011-07-31 00:11:07 +00:00
|
|
|
// since without them it will be much harder
|
|
|
|
// to find things.
|
|
|
|
action->setIconVisibleInMenu( true );
|
|
|
|
}
|
2009-04-12 16:22:42 +00:00
|
|
|
menu.addAction( action );
|
|
|
|
|
|
|
|
tableOfContents[ action ] = *i;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-07-31 00:11:07 +00:00
|
|
|
if( maxDictionaryRefsReached )
|
|
|
|
break;
|
2009-04-12 16:22:42 +00:00
|
|
|
}
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2013-05-30 02:18:28 +00:00
|
|
|
menu.addSeparator();
|
2022-09-08 13:11:25 +00:00
|
|
|
if(!popupView||cfg.pinPopupWindow)
|
|
|
|
menu.addAction( &inspectAction );
|
2013-05-30 02:18:28 +00:00
|
|
|
|
2009-01-28 20:55:45 +00:00
|
|
|
if ( !menu.isEmpty() )
|
|
|
|
{
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( this, &ArticleView::closePopupMenu, &menu, &QWidget::close );
|
2023-03-17 09:42:44 +00:00
|
|
|
QAction * result = menu.exec( webview->mapToGlobal( pos ) );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2009-05-29 19:48:50 +00:00
|
|
|
if ( !result )
|
|
|
|
return;
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( result == followLink )
|
|
|
|
openLink( targetUrl, webview->url(), getCurrentArticle(), contexts );
|
|
|
|
else if( result == followLinkExternal )
|
2021-12-29 12:09:29 +00:00
|
|
|
QDesktopServices::openUrl( targetUrl );
|
2023-03-17 09:42:44 +00:00
|
|
|
else if( result == lookupSelection )
|
|
|
|
showDefinition( text, getGroup( webview->url() ), getCurrentArticle() );
|
|
|
|
else if( result == saveBookmark ) {
|
2022-06-01 15:40:00 +00:00
|
|
|
emit saveBookmarkSignal( text.left( 60 ) );
|
2022-06-01 15:11:41 +00:00
|
|
|
}
|
2023-03-17 09:42:44 +00:00
|
|
|
else if( result == sendToAnkiAction ) {
|
|
|
|
sendToAnki( webview->title(), webview->selectedText(), translateLine->text() );
|
2022-05-21 06:03:26 +00:00
|
|
|
}
|
2009-02-08 15:49:17 +00:00
|
|
|
else
|
2009-05-14 19:27:19 +00:00
|
|
|
if ( result == lookupSelectionGr && groupComboBox )
|
|
|
|
showDefinition( selectedText, groupComboBox->getCurrentGroup(), QString() );
|
|
|
|
else
|
2012-09-12 14:11:30 +00:00
|
|
|
if ( result == addWordToHistoryAction )
|
|
|
|
emit forceAddWordToHistory( selectedText );
|
2023-03-17 09:42:44 +00:00
|
|
|
if( result == addHeaderToHistoryAction )
|
|
|
|
emit forceAddWordToHistory( webview->title() );
|
|
|
|
else if( result == sendWordToInputLineAction )
|
2012-11-26 13:13:13 +00:00
|
|
|
emit sendWordToInputLine( selectedText );
|
2023-03-17 09:42:44 +00:00
|
|
|
else if( !popupView && result == followLinkNewTab )
|
|
|
|
emit openLinkInNewTab( targetUrl, webview->url(), getCurrentArticle(), contexts );
|
|
|
|
else if( !popupView && result == lookupSelectionNewTab )
|
|
|
|
emit showDefinitionInNewTab( selectedText, getGroup( webview->url() ), getCurrentArticle(), Contexts() );
|
|
|
|
else if( !popupView && result == lookupSelectionNewTabGr && groupComboBox )
|
|
|
|
emit showDefinitionInNewTab( selectedText, groupComboBox->getCurrentGroup(), QString(), Contexts() );
|
|
|
|
else if( result == saveImageAction || result == saveSoundAction ) {
|
2022-01-09 14:18:53 +00:00
|
|
|
QUrl url = ( result == saveImageAction ) ? imageUrl : targetUrl;
|
2013-05-27 11:18:13 +00:00
|
|
|
QString savePath;
|
|
|
|
QString fileName;
|
|
|
|
|
|
|
|
if ( cfg.resourceSavePath.isEmpty() )
|
|
|
|
savePath = QDir::homePath();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
savePath = QDir::fromNativeSeparators( cfg.resourceSavePath );
|
|
|
|
if ( !QDir( savePath ).exists() )
|
|
|
|
savePath = QDir::homePath();
|
|
|
|
}
|
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
QString name = Utils::Url::path( url ).section( '/', -1 );
|
2013-05-27 11:18:13 +00:00
|
|
|
|
|
|
|
if ( result == saveSoundAction )
|
|
|
|
{
|
|
|
|
// Audio data
|
2022-11-16 13:09:30 +00:00
|
|
|
// if ( name.indexOf( '.' ) < 0 )
|
|
|
|
// name += ".wav";
|
2013-05-27 11:18:13 +00:00
|
|
|
|
|
|
|
fileName = savePath + "/" + name;
|
|
|
|
fileName = QFileDialog::getSaveFileName( parentWidget(), tr( "Save sound" ),
|
|
|
|
fileName,
|
2022-11-17 06:09:11 +00:00
|
|
|
tr( "Sound files (*.wav *.ogg *.oga *.mp3 *.mp4 *.aac *.flac *.mid *.wv *.ape *.spx);;All files (*.*)" ) );
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Image data
|
|
|
|
|
|
|
|
// Check for babylon image name
|
|
|
|
if ( name[ 0 ] == '\x1E' )
|
|
|
|
name.remove( 0, 1 );
|
|
|
|
if ( name.length() && 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 (*.*)" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !fileName.isEmpty() )
|
|
|
|
{
|
|
|
|
QFileInfo fileInfo( fileName );
|
|
|
|
emit storeResourceSavePath( QDir::toNativeSeparators( fileInfo.absoluteDir().absolutePath() ) );
|
2023-03-17 09:42:44 +00:00
|
|
|
saveResource( url, webview->url(), fileName );
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
|
|
|
}
|
2013-02-22 12:44:23 +00:00
|
|
|
else
|
2009-02-08 15:49:17 +00:00
|
|
|
{
|
2011-07-31 00:11:07 +00:00
|
|
|
if ( !popupView && result == maxDictionaryRefsAction )
|
|
|
|
emit showDictsPane();
|
|
|
|
|
2009-04-12 16:22:42 +00:00
|
|
|
// Match against table of contents
|
|
|
|
QString id = tableOfContents[ result ];
|
|
|
|
|
|
|
|
if ( id.size() )
|
2021-06-29 08:59:16 +00:00
|
|
|
setCurrentArticle( scrollToFromDictionaryId( id ), true );
|
2009-02-08 15:49:17 +00:00
|
|
|
}
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2022-03-30 07:21:16 +00:00
|
|
|
qDebug()<< "title = "<< r->title();
|
2021-09-22 04:01:17 +00:00
|
|
|
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
void ArticleView::resourceDownloadFinished()
|
|
|
|
{
|
|
|
|
if ( resourceDownloadRequests.empty() )
|
|
|
|
return; // Stray signal
|
|
|
|
|
|
|
|
// Find any finished resources
|
|
|
|
for( list< sptr< Dictionary::DataRequest > >::iterator i =
|
|
|
|
resourceDownloadRequests.begin(); i != resourceDownloadRequests.end(); )
|
|
|
|
{
|
|
|
|
if ( (*i)->isFinished() )
|
|
|
|
{
|
2022-11-01 09:40:20 +00:00
|
|
|
if ( (*i)->dataSize() >= 0 )
|
2009-03-26 19:00:08 +00:00
|
|
|
{
|
|
|
|
// Ok, got one finished, all others are irrelevant now
|
|
|
|
|
|
|
|
vector< char > const & data = (*i)->getFullData();
|
|
|
|
|
2010-05-29 11:33:04 +00:00
|
|
|
if ( resourceDownloadUrl.scheme() == "gdau" ||
|
|
|
|
Dictionary::WebMultimediaDownload::isAudioUrl( resourceDownloadUrl ) )
|
2009-03-26 19:00:08 +00:00
|
|
|
{
|
2010-01-02 18:16:22 +00:00
|
|
|
// Audio data
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( audioPlayer.data(),
|
|
|
|
&AudioPlayerInterface::error,
|
|
|
|
this,
|
|
|
|
&ArticleView::audioPlayerError,
|
|
|
|
Qt::UniqueConnection );
|
2018-03-22 17:07:10 +00:00
|
|
|
QString errorMessage = audioPlayer->play( data.data(), data.size() );
|
|
|
|
if( !errorMessage.isEmpty() )
|
|
|
|
QMessageBox::critical( this, "GoldenDict", tr( "Failed to play sound file: %1" ).arg( errorMessage ) );
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Create a temporary file
|
2017-03-13 14:38:27 +00:00
|
|
|
// Remove the ones previously used, if any
|
2009-03-26 19:00:08 +00:00
|
|
|
cleanupTemp();
|
2017-03-13 14:38:27 +00:00
|
|
|
QString fileName;
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
{
|
|
|
|
QTemporaryFile tmp(
|
|
|
|
QDir::temp().filePath( "XXXXXX-" + resourceDownloadUrl.path().section( '/', -1 ) ), this );
|
|
|
|
|
2012-10-31 13:58:35 +00:00
|
|
|
if ( !tmp.open() || (size_t) tmp.write( &data.front(), data.size() ) != data.size() )
|
2009-03-26 19:00:08 +00:00
|
|
|
{
|
2013-02-01 12:36:01 +00:00
|
|
|
QMessageBox::critical( this, "GoldenDict", tr( "Failed to create temporary file." ) );
|
2009-03-26 19:00:08 +00:00
|
|
|
return;
|
|
|
|
}
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2009-03-26 19:00:08 +00:00
|
|
|
tmp.setAutoRemove( false );
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2017-03-13 14:38:27 +00:00
|
|
|
desktopOpenedTempFiles.insert( fileName = tmp.fileName() );
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
2010-09-16 18:53:39 +00:00
|
|
|
|
2017-03-13 14:38:27 +00:00
|
|
|
if ( !QDesktopServices::openUrl( QUrl::fromLocalFile( fileName ) ) )
|
2013-02-01 12:36:01 +00:00
|
|
|
QMessageBox::critical( this, "GoldenDict",
|
2017-03-13 14:38:27 +00:00
|
|
|
tr( "Failed to auto-open resource file, try opening manually: %1." ).arg( fileName ) );
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ok, whatever it was, it's finished. Remove this and any other
|
|
|
|
// requests and finish.
|
|
|
|
|
|
|
|
resourceDownloadRequests.clear();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-05-29 05:08:37 +00:00
|
|
|
// This one had no data. Erase it.
|
2009-03-26 19:00:08 +00:00
|
|
|
resourceDownloadRequests.erase( i++ );
|
|
|
|
}
|
|
|
|
}
|
2017-04-27 20:55:53 +00:00
|
|
|
else // Unfinished, wait.
|
|
|
|
break;
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( resourceDownloadRequests.empty() )
|
|
|
|
{
|
2022-12-11 11:15:30 +00:00
|
|
|
// emit statusBarMessage(
|
|
|
|
// tr("WARNING: %1").arg(tr("The referenced resource failed to download.")),
|
|
|
|
// 10000, QPixmap(":/icons/error.svg"));
|
2009-03-26 19:00:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-05 10:22:12 +00:00
|
|
|
void ArticleView::audioPlayerError( QString const & message )
|
|
|
|
{
|
2022-01-27 00:23:57 +00:00
|
|
|
emit statusBarMessage(tr("WARNING: Audio Player: %1").arg(message),
|
|
|
|
10000, QPixmap(":/icons/error.svg"));
|
2013-05-05 10:22:12 +00:00
|
|
|
}
|
|
|
|
|
2009-05-12 10:52:11 +00:00
|
|
|
void ArticleView::pasteTriggered()
|
|
|
|
{
|
2021-06-10 16:13:11 +00:00
|
|
|
Config::InputPhrase phrase = cfg.preferences.sanitizeInputPhrase( QApplication::clipboard()->text() );
|
2009-05-12 10:52:11 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( phrase.isValid() ) {
|
|
|
|
unsigned groupId = getGroup( webview->url() );
|
|
|
|
if( groupId == 0 ) {
|
2013-08-22 16:05:39 +00:00
|
|
|
// We couldn't figure out the group out of the URL,
|
|
|
|
// so let's try the currently selected group.
|
|
|
|
groupId = groupComboBox->getCurrentGroup();
|
|
|
|
}
|
2021-06-10 16:13:11 +00:00
|
|
|
showDefinition( phrase, groupId, getCurrentArticle() );
|
2013-08-22 16:05:39 +00:00
|
|
|
}
|
2009-05-12 10:52:11 +00:00
|
|
|
}
|
|
|
|
|
2009-05-15 14:11:54 +00:00
|
|
|
void ArticleView::moveOneArticleUp()
|
|
|
|
{
|
|
|
|
QString current = getCurrentArticle();
|
|
|
|
|
|
|
|
if ( current.size() )
|
|
|
|
{
|
|
|
|
QStringList lst = getArticlesList();
|
|
|
|
|
2021-06-29 08:59:16 +00:00
|
|
|
int idx = lst.indexOf( dictionaryIdFromScrollTo( current ) );
|
2009-05-15 14:11:54 +00:00
|
|
|
|
|
|
|
if ( idx != -1 )
|
|
|
|
{
|
|
|
|
--idx;
|
|
|
|
|
|
|
|
if ( idx < 0 )
|
|
|
|
idx = lst.size() - 1;
|
|
|
|
|
2021-06-29 08:59:16 +00:00
|
|
|
setCurrentArticle( scrollToFromDictionaryId( lst[ idx ] ), true );
|
2009-05-15 14:11:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::moveOneArticleDown()
|
|
|
|
{
|
|
|
|
QString current = getCurrentArticle();
|
2022-02-22 16:27:30 +00:00
|
|
|
QString currentDictId = dictionaryIdFromScrollTo( current );
|
|
|
|
QStringList lst = getArticlesList();
|
|
|
|
// if current article is empty .use the first as default.
|
|
|
|
if( currentDictId.isEmpty() && !lst.isEmpty() )
|
2009-05-15 14:11:54 +00:00
|
|
|
{
|
2022-02-22 16:27:30 +00:00
|
|
|
currentDictId = lst[ 0 ];
|
|
|
|
}
|
2009-05-15 14:11:54 +00:00
|
|
|
|
2022-02-22 16:27:30 +00:00
|
|
|
int idx = lst.indexOf( currentDictId );
|
2009-05-15 14:11:54 +00:00
|
|
|
|
2022-02-22 16:27:30 +00:00
|
|
|
if( idx != -1 )
|
|
|
|
{
|
|
|
|
idx = ( idx + 1 ) % lst.size();
|
2009-05-15 14:11:54 +00:00
|
|
|
|
2022-02-22 16:27:30 +00:00
|
|
|
setCurrentArticle( scrollToFromDictionaryId( lst[ idx ] ), true );
|
2009-05-15 14:11:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-16 11:14:43 +00:00
|
|
|
void ArticleView::openSearch()
|
|
|
|
{
|
2014-04-16 16:18:28 +00:00
|
|
|
if( !isVisible() )
|
|
|
|
return;
|
|
|
|
|
2014-04-22 13:47:02 +00:00
|
|
|
if( ftsSearchIsOpened )
|
|
|
|
closeSearch();
|
|
|
|
|
2009-05-16 11:14:43 +00:00
|
|
|
if ( !searchIsOpened )
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
searchPanel->show();
|
|
|
|
searchText->setText( getTitle() );
|
2009-05-16 11:14:43 +00:00
|
|
|
searchIsOpened = true;
|
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
searchText->setFocus();
|
|
|
|
searchText->selectAll();
|
2009-05-16 11:14:43 +00:00
|
|
|
|
|
|
|
// Clear any current selection
|
2023-03-17 09:42:44 +00:00
|
|
|
if( webview->selectedText().size() ) {
|
|
|
|
webview->page()->runJavaScript( "window.getSelection().removeAllRanges();_=0;" );
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchText->property( "noResults" ).toBool() ) {
|
|
|
|
searchText->setProperty( "noResults", false );
|
2009-05-16 18:04:21 +00:00
|
|
|
|
|
|
|
// Reload stylesheet
|
2010-11-15 15:22:35 +00:00
|
|
|
reloadStyleSheet();
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_searchPrevious_clicked()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchIsOpened )
|
2013-01-22 21:04:45 +00:00
|
|
|
performFindOperation( false, true );
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_searchNext_clicked()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchIsOpened )
|
2013-01-22 21:04:45 +00:00
|
|
|
performFindOperation( false, false );
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_searchText_textEdited()
|
|
|
|
{
|
|
|
|
performFindOperation( true, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_searchText_returnPressed()
|
|
|
|
{
|
|
|
|
on_searchNext_clicked();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_searchCloseButton_clicked()
|
|
|
|
{
|
|
|
|
closeSearch();
|
|
|
|
}
|
|
|
|
|
2011-06-12 23:59:35 +00:00
|
|
|
void ArticleView::on_searchCaseSensitive_clicked()
|
|
|
|
{
|
2022-06-16 18:13:56 +00:00
|
|
|
performFindOperation( true, false );
|
2011-06-12 23:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_highlightAllButton_clicked()
|
|
|
|
{
|
|
|
|
performFindOperation( false, false, true );
|
|
|
|
}
|
|
|
|
|
2022-01-18 15:04:27 +00:00
|
|
|
//the id start with "gdform-"
|
2011-07-03 12:27:08 +00:00
|
|
|
void ArticleView::onJsActiveArticleChanged(QString const & id)
|
|
|
|
{
|
2021-06-29 08:59:16 +00:00
|
|
|
if ( !isScrollTo( id ) )
|
2011-07-03 12:27:08 +00:00
|
|
|
return; // Incorrect id
|
|
|
|
|
2022-02-25 14:48:43 +00:00
|
|
|
QString dictId = dictionaryIdFromScrollTo( id );
|
|
|
|
setActiveArticleId( dictId );
|
|
|
|
emit activeArticleChanged( this, dictId );
|
2011-07-03 12:27:08 +00:00
|
|
|
}
|
|
|
|
|
2017-03-13 14:38:27 +00:00
|
|
|
void ArticleView::doubleClicked( QPoint pos )
|
2010-04-08 20:37:59 +00:00
|
|
|
{
|
|
|
|
// We might want to initiate translation of the selected word
|
2022-02-25 15:33:34 +00:00
|
|
|
audioPlayer->stop();
|
2023-03-17 09:42:44 +00:00
|
|
|
if( cfg.preferences.doubleClickTranslates ) {
|
|
|
|
QString selectedText = webview->selectedText();
|
2022-05-08 13:02:23 +00:00
|
|
|
|
|
|
|
// ignore empty word;
|
|
|
|
if( selectedText.isEmpty() )
|
|
|
|
return;
|
|
|
|
|
2022-03-30 07:21:16 +00:00
|
|
|
emit sendWordToInputLine( selectedText );
|
2010-04-08 20:37:59 +00:00
|
|
|
// Do some checks to make sure there's a sensible selection indeed
|
|
|
|
if ( Folding::applyWhitespaceOnly( gd::toWString( selectedText ) ).size() &&
|
2013-01-18 11:00:38 +00:00
|
|
|
selectedText.size() < 60 )
|
2010-04-08 20:37:59 +00:00
|
|
|
{
|
|
|
|
// Initiate translation
|
2010-09-16 18:53:39 +00:00
|
|
|
Qt::KeyboardModifiers kmod = QApplication::keyboardModifiers();
|
2023-03-17 09:42:44 +00:00
|
|
|
if( kmod & ( Qt::ControlModifier | Qt::ShiftModifier ) ) { // open in new tab
|
|
|
|
emit showDefinitionInNewTab( selectedText, getGroup( webview->url() ), getCurrentArticle(), Contexts() );
|
2010-09-16 18:53:39 +00:00
|
|
|
}
|
2023-03-17 09:42:44 +00:00
|
|
|
else {
|
|
|
|
QUrl const & ref = webview->url();
|
2014-04-22 18:45:42 +00:00
|
|
|
|
2021-11-27 07:17:33 +00:00
|
|
|
if( Utils::Url::hasQueryItem( ref, "dictionaries" ) )
|
2014-04-22 18:45:42 +00:00
|
|
|
{
|
2021-11-27 07:17:33 +00:00
|
|
|
QStringList dictsList = Utils::Url::queryItemValue(ref, "dictionaries" )
|
2021-12-29 14:29:06 +00:00
|
|
|
.split( ",", Qt::SkipEmptyParts );
|
2018-04-10 14:49:52 +00:00
|
|
|
showDefinition( selectedText, dictsList, QRegExp(), getGroup( ref ), false );
|
2014-04-22 18:45:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
showDefinition( selectedText, getGroup( ref ), getCurrentArticle() );
|
|
|
|
}
|
2010-04-08 20:37:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-12 23:59:35 +00:00
|
|
|
void ArticleView::performFindOperation( bool restart, bool backwards, bool checkHighlight )
|
2009-05-16 11:14:43 +00:00
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
QString text = searchText->text();
|
2009-05-16 11:14:43 +00:00
|
|
|
|
2011-06-12 23:59:35 +00:00
|
|
|
if ( restart || checkHighlight )
|
2009-05-16 11:14:43 +00:00
|
|
|
{
|
2011-06-12 23:59:35 +00:00
|
|
|
if( restart ) {
|
|
|
|
// Anyone knows how we reset the search position?
|
|
|
|
// For now we resort to this hack:
|
2023-03-17 09:42:44 +00:00
|
|
|
if( webview->selectedText().size() ) {
|
|
|
|
webview->page()->runJavaScript( "window.getSelection().removeAllRanges();_=0;" );
|
2011-06-12 23:59:35 +00:00
|
|
|
}
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
2011-06-12 23:59:35 +00:00
|
|
|
|
2021-07-06 13:01:50 +00:00
|
|
|
QWebEnginePage::FindFlags f( 0 );
|
2011-06-12 23:59:35 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchCaseSensitive->isChecked() )
|
2021-07-06 13:01:50 +00:00
|
|
|
f |= QWebEnginePage::FindCaseSensitively;
|
2011-06-12 23:59:35 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( "", f );
|
2011-06-12 23:59:35 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( highlightAllButton->isChecked() )
|
|
|
|
webview->findText( text, f );
|
2011-06-12 23:59:35 +00:00
|
|
|
|
|
|
|
if( checkHighlight )
|
|
|
|
return;
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
|
|
|
|
2021-07-06 13:01:50 +00:00
|
|
|
QWebEnginePage::FindFlags f( 0 );
|
2009-05-16 11:14:43 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchCaseSensitive->isChecked() )
|
2021-07-06 13:01:50 +00:00
|
|
|
f |= QWebEnginePage::FindCaseSensitively;
|
2009-05-16 11:14:43 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( backwards )
|
2021-07-06 13:01:50 +00:00
|
|
|
f |= QWebEnginePage::FindBackward;
|
2009-05-16 11:14:43 +00:00
|
|
|
|
2022-06-01 15:11:41 +00:00
|
|
|
findText( text,
|
|
|
|
f,
|
2022-08-17 18:36:17 +00:00
|
|
|
[ text, this ]( bool match )
|
2022-06-01 15:11:41 +00:00
|
|
|
{
|
|
|
|
bool setMark = !text.isEmpty() && !match;
|
2009-05-16 11:14:43 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchText->property( "noResults" ).toBool() != setMark ) {
|
|
|
|
searchText->setProperty( "noResults", setMark );
|
2009-05-16 18:04:21 +00:00
|
|
|
|
2022-06-01 15:11:41 +00:00
|
|
|
// Reload stylesheet
|
|
|
|
reloadStyleSheet();
|
|
|
|
}
|
|
|
|
} );
|
2010-11-15 15:22:35 +00:00
|
|
|
}
|
|
|
|
|
2022-06-01 15:11:41 +00:00
|
|
|
void ArticleView::findText( QString & text,
|
|
|
|
const QWebEnginePage::FindFlags & f,
|
|
|
|
const std::function< void( bool match ) > & callback )
|
2021-07-06 13:01:50 +00:00
|
|
|
{
|
2022-06-01 15:11:41 +00:00
|
|
|
#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( text, f, [ callback ]( const QWebEngineFindTextResult & result ) {
|
|
|
|
auto r = result.numberOfMatches() > 0;
|
|
|
|
if( callback )
|
|
|
|
callback( r );
|
|
|
|
} );
|
2022-02-27 05:17:37 +00:00
|
|
|
#else
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( text, f, [ callback ]( bool result ) {
|
|
|
|
if( callback )
|
|
|
|
callback( result );
|
|
|
|
} );
|
2022-02-27 05:17:37 +00:00
|
|
|
#endif
|
2021-07-06 13:01:50 +00:00
|
|
|
}
|
|
|
|
|
2010-11-15 15:22:35 +00:00
|
|
|
void ArticleView::reloadStyleSheet()
|
|
|
|
{
|
|
|
|
for( QWidget * w = parentWidget(); w; w = w->parentWidget() )
|
|
|
|
{
|
|
|
|
if ( w->styleSheet().size() )
|
2009-05-16 18:04:21 +00:00
|
|
|
{
|
2010-11-15 15:22:35 +00:00
|
|
|
w->setStyleSheet( w->styleSheet() );
|
|
|
|
break;
|
2009-05-16 18:04:21 +00:00
|
|
|
}
|
2009-05-16 11:14:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-15 15:22:35 +00:00
|
|
|
|
2009-05-16 11:14:43 +00:00
|
|
|
bool ArticleView::closeSearch()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
if( searchIsOpened ) {
|
|
|
|
searchPanel->hide();
|
|
|
|
webview->setFocus();
|
2009-05-16 11:14:43 +00:00
|
|
|
searchIsOpened = false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2014-04-22 13:47:02 +00:00
|
|
|
else
|
|
|
|
if( ftsSearchIsOpened )
|
|
|
|
{
|
|
|
|
allMatches.clear();
|
|
|
|
uniqueMatches.clear();
|
|
|
|
ftsPosition = 0;
|
|
|
|
ftsSearchIsOpened = false;
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
ftsSearchPanel->hide();
|
|
|
|
webview->setFocus();
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2021-07-06 13:01:50 +00:00
|
|
|
QWebEnginePage::FindFlags flags ( 0 );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( "", flags );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2009-05-16 11:14:43 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
bool ArticleView::isSearchOpened() { return searchIsOpened; }
|
2011-06-29 19:12:46 +00:00
|
|
|
|
2009-01-28 20:55:45 +00:00
|
|
|
void ArticleView::showEvent( QShowEvent * ev )
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
QWidget::showEvent( ev );
|
2009-01-28 20:55:45 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( !searchIsOpened )
|
|
|
|
searchPanel->hide();
|
2014-04-22 13:47:02 +00:00
|
|
|
|
|
|
|
if( !ftsSearchIsOpened )
|
2023-03-17 09:42:44 +00:00
|
|
|
ftsSearchPanel->hide();
|
2009-01-28 20:55:45 +00:00
|
|
|
}
|
2012-09-16 10:19:47 +00:00
|
|
|
|
|
|
|
void ArticleView::receiveExpandOptionalParts( bool expand )
|
|
|
|
{
|
|
|
|
if( expandOptionalParts != expand )
|
2022-06-11 17:34:52 +00:00
|
|
|
switchExpandOptionalParts();
|
2012-09-16 10:19:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::switchExpandOptionalParts()
|
|
|
|
{
|
|
|
|
expandOptionalParts = !expandOptionalParts;
|
|
|
|
emit setExpandMode( expandOptionalParts );
|
|
|
|
reload();
|
|
|
|
}
|
2012-09-18 23:01:31 +00:00
|
|
|
|
2013-05-15 13:52:47 +00:00
|
|
|
void ArticleView::copyAsText()
|
|
|
|
{
|
2023-03-17 09:42:44 +00:00
|
|
|
QString text = webview->selectedText();
|
2013-05-15 13:52:47 +00:00
|
|
|
if( !text.isEmpty() )
|
|
|
|
QApplication::clipboard()->setText( text );
|
|
|
|
}
|
|
|
|
|
2014-04-22 13:47:02 +00:00
|
|
|
void ArticleView::highlightFTSResults()
|
|
|
|
{
|
|
|
|
closeSearch();
|
|
|
|
|
|
|
|
// Clear any current selection
|
2023-03-17 09:42:44 +00:00
|
|
|
if( webview->selectedText().size() ) {
|
|
|
|
webview->page()->runJavaScript( "window.getSelection().removeAllRanges();_=0;" );
|
2014-04-22 13:47:02 +00:00
|
|
|
}
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->toPlainText(
|
|
|
|
[ & ]( const QString pageText ) {
|
|
|
|
const QUrl & url = webview->url();
|
2022-05-19 12:28:52 +00:00
|
|
|
|
|
|
|
QString regString = Utils::Url::queryItemValue( url, "regexp" );
|
2022-07-02 11:13:53 +00:00
|
|
|
if( regString.isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool ignoreDiacritics = Utils::Url::hasQueryItem( url, "ignore_diacritics" );
|
2023-03-17 09:42:44 +00:00
|
|
|
|
|
|
|
if( ignoreDiacritics )
|
2022-05-19 12:28:52 +00:00
|
|
|
regString = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( regString ) ) );
|
|
|
|
else
|
|
|
|
regString = regString.remove( AccentMarkHandler::accentMark() );
|
|
|
|
|
2022-06-16 13:03:58 +00:00
|
|
|
//<div><i>watch</i>out</div> to plainText will return "watchout".
|
|
|
|
//if application goes here,that means the article text must contains the search text.
|
|
|
|
//whole word match regString will contain \b . can not match the above senario.
|
|
|
|
//workaround ,remove \b from the regstring="(\bwatch\b)"
|
|
|
|
regString.remove( QRegularExpression( "\\\\b" ) );
|
|
|
|
|
2022-05-19 12:28:52 +00:00
|
|
|
QRegularExpression regexp;
|
|
|
|
if( Utils::Url::hasQueryItem( url, "wildcards" ) )
|
|
|
|
regexp.setPattern( wildcardsToRegexp( regString ) );
|
|
|
|
else
|
|
|
|
regexp.setPattern( regString );
|
|
|
|
|
|
|
|
QRegularExpression::PatternOptions patternOptions =
|
|
|
|
QRegularExpression::DotMatchesEverythingOption | QRegularExpression::UseUnicodePropertiesOption |
|
|
|
|
QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption;
|
|
|
|
if( !Utils::Url::hasQueryItem( url, "matchcase" ) )
|
|
|
|
patternOptions |= QRegularExpression::CaseInsensitiveOption;
|
|
|
|
regexp.setPatternOptions( patternOptions );
|
|
|
|
|
|
|
|
if( regexp.pattern().isEmpty() || !regexp.isValid() )
|
|
|
|
return;
|
2022-11-29 03:54:31 +00:00
|
|
|
sptr< AccentMarkHandler > marksHandler = ignoreDiacritics ? std::make_shared<DiacriticsHandler>() : std::make_shared<AccentMarkHandler>();
|
2022-05-19 12:28:52 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
marksHandler->setText( pageText );
|
2014-04-24 14:12:00 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
QRegularExpressionMatchIterator it = regexp.globalMatch( marksHandler->normalizedText() );
|
|
|
|
while( it.hasNext() )
|
|
|
|
{
|
|
|
|
QRegularExpressionMatch match = it.next();
|
2018-09-25 14:58:51 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
// Mirror pos and matched length to original string
|
|
|
|
int pos = match.capturedStart();
|
|
|
|
int spos = marksHandler->mirrorPosition( pos );
|
|
|
|
int matched = marksHandler->mirrorPosition( pos + match.capturedLength() ) - spos;
|
2018-09-25 14:58:51 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
// Add mark pos (if presented)
|
|
|
|
while( spos + matched < pageText.length() && pageText[ spos + matched ].category() == QChar::Mark_NonSpacing )
|
|
|
|
matched++;
|
2018-09-25 14:58:51 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
if( matched > FTS::MaxMatchLengthForHighlightResults )
|
|
|
|
{
|
|
|
|
gdWarning( "ArticleView::highlightFTSResults(): Too long match - skipped (matched length %i, allowed %i)",
|
|
|
|
match.capturedLength(),
|
|
|
|
FTS::MaxMatchLengthForHighlightResults );
|
2022-03-28 09:29:00 +00:00
|
|
|
}
|
2022-03-30 15:08:24 +00:00
|
|
|
else
|
|
|
|
allMatches.append( pageText.mid( spos, matched ) );
|
|
|
|
}
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
ftsSearchMatchCase = Utils::Url::hasQueryItem( url, "matchcase" );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2022-05-19 12:28:52 +00:00
|
|
|
QWebEnginePage::FindFlags flags( QWebEnginePage::FindBackward );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
if( ftsSearchMatchCase )
|
|
|
|
flags |= QWebEnginePage::FindCaseSensitively;
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( allMatches.isEmpty() )
|
|
|
|
ftsSearchStatusLabel->setText( searchStatusMessageNoMatches() );
|
|
|
|
else {
|
|
|
|
// highlightAllFtsOccurences( flags );
|
|
|
|
webview->findText( allMatches.at( 0 ), flags );
|
|
|
|
// if( webview->findText( allMatches.at( 0 ), flags ) )
|
2022-06-15 23:53:14 +00:00
|
|
|
// {
|
2023-03-17 09:42:44 +00:00
|
|
|
// webview->page()->runJavaScript(
|
2022-06-15 23:53:14 +00:00
|
|
|
// QString( "%1=window.getSelection().getRangeAt(0);_=0;" ).arg( rangeVarName ) );
|
|
|
|
// }
|
2022-07-10 04:39:12 +00:00
|
|
|
Q_ASSERT( ftsPosition == 0 );
|
2023-03-17 09:42:44 +00:00
|
|
|
ftsSearchStatusLabel->setText( searchStatusMessage( 1, allMatches.size() ) );
|
2022-03-30 15:08:24 +00:00
|
|
|
}
|
2014-04-22 13:47:02 +00:00
|
|
|
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
ftsSearchPanel->show();
|
|
|
|
ftsSearchPrevious->setEnabled( false );
|
|
|
|
ftsSearchNext->setEnabled( allMatches.size() > 1 );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2022-03-30 15:08:24 +00:00
|
|
|
ftsSearchIsOpened = true;
|
|
|
|
} );
|
2021-07-06 13:01:50 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 12:21:40 +00:00
|
|
|
void ArticleView::highlightAllFtsOccurences( QWebEnginePage::FindFlags flags )
|
2022-05-22 19:12:30 +00:00
|
|
|
{
|
2022-05-22 19:21:00 +00:00
|
|
|
// Usually allMatches contains mostly duplicates. Thus searching for each element of
|
|
|
|
// allMatches to highlight them takes a long time => collect unique elements into a
|
|
|
|
// set and search for them instead.
|
|
|
|
// Don't use QList::toSet() or QSet's range constructor because they reserve space
|
|
|
|
// for QList::size() elements, whereas the final QSet size is likely 1 or 2.
|
|
|
|
QSet< QString > uniqueMatches;
|
|
|
|
for( int x = 0; x < allMatches.size(); ++x )
|
|
|
|
{
|
|
|
|
QString const & match = allMatches.at( x );
|
|
|
|
// Consider words that differ only in case equal if the search is case-insensitive.
|
|
|
|
uniqueMatches.insert( ftsSearchMatchCase ? match : match.toLower() );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( QSet< QString >::const_iterator it = uniqueMatches.constBegin(); it != uniqueMatches.constEnd(); ++it )
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( *it, flags );
|
2022-05-22 19:12:30 +00:00
|
|
|
}
|
|
|
|
|
2022-01-08 06:51:24 +00:00
|
|
|
void ArticleView::setActiveDictIds(ActiveDictIds ad) {
|
2022-11-04 13:23:42 +00:00
|
|
|
if (ad.word == currentWord || historyMode) {
|
2022-07-16 22:54:53 +00:00
|
|
|
// ignore all other signals.
|
|
|
|
qDebug() << "receive dicts, current word:" << currentWord << ad.word << ":" << ad.dictIds;
|
2022-01-08 13:45:10 +00:00
|
|
|
currentActiveDictIds << ad.dictIds;
|
|
|
|
currentActiveDictIds.removeDuplicates();
|
2022-01-09 04:54:50 +00:00
|
|
|
emit updateFoundInDictsList();
|
2022-01-08 06:51:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 22:54:53 +00:00
|
|
|
void ArticleView::dictionaryClear( ActiveDictIds ad )
|
|
|
|
{
|
|
|
|
// ignore all other signals.
|
|
|
|
if( ad.word == currentWord )
|
|
|
|
{
|
|
|
|
qDebug() << "clear current dictionaries:" << currentWord;
|
|
|
|
currentActiveDictIds.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:45:16 +00:00
|
|
|
//todo ,futher refinement?
|
2014-04-22 13:47:02 +00:00
|
|
|
void ArticleView::performFtsFindOperation( bool backwards )
|
|
|
|
{
|
|
|
|
if( !ftsSearchIsOpened )
|
|
|
|
return;
|
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( allMatches.isEmpty() ) {
|
|
|
|
ftsSearchStatusLabel->setText( searchStatusMessageNoMatches() );
|
|
|
|
ftsSearchNext->setEnabled( false );
|
|
|
|
ftsSearchPrevious->setEnabled( false );
|
2014-04-22 13:47:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-06 13:01:50 +00:00
|
|
|
QWebEnginePage::FindFlags flags( 0 );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
|
|
|
if( ftsSearchMatchCase )
|
2021-07-06 13:01:50 +00:00
|
|
|
flags |= QWebEnginePage::FindCaseSensitively;
|
2014-04-22 13:47:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Restore saved highlighted selection
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->page()->runJavaScript(
|
|
|
|
QString( "var sel=window.getSelection();sel.removeAllRanges();sel.addRange(%1);_=0;" ).arg( rangeVarName ) );
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
if( backwards ) {
|
|
|
|
if( ftsPosition > 0 ) {
|
|
|
|
ftsPosition -= 1;
|
|
|
|
}
|
2022-02-27 05:17:37 +00:00
|
|
|
#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( allMatches.at( ftsPosition ),
|
|
|
|
flags | QWebEnginePage::FindBackward,
|
|
|
|
[ this ]( const QWebEngineFindTextResult & result ) {
|
|
|
|
if( result.numberOfMatches() == 0 )
|
|
|
|
return;
|
|
|
|
ftsSearchPrevious->setEnabled( true );
|
|
|
|
if( !ftsSearchNext->isEnabled() )
|
|
|
|
ftsSearchNext->setEnabled( true );
|
|
|
|
} );
|
2022-02-27 05:17:37 +00:00
|
|
|
#else
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( allMatches.at( ftsPosition ), flags | QWebEnginePage::FindBackward, [ this ]( bool res ) {
|
|
|
|
ftsSearchPrevious->setEnabled( res );
|
|
|
|
if( !ftsSearchNext->isEnabled() )
|
|
|
|
ftsSearchNext->setEnabled( res );
|
|
|
|
} );
|
2022-02-27 05:17:37 +00:00
|
|
|
#endif
|
2023-03-17 09:42:44 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if( ftsPosition < allMatches.size() - 1 ) {
|
|
|
|
ftsPosition += 1;
|
|
|
|
}
|
2022-02-27 05:17:37 +00:00
|
|
|
#if( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( allMatches.at( ftsPosition ), flags, [ this ]( const QWebEngineFindTextResult & result ) {
|
|
|
|
if( result.numberOfMatches() == 0 )
|
|
|
|
return;
|
|
|
|
ftsSearchNext->setEnabled( true );
|
|
|
|
if( !ftsSearchPrevious->isEnabled() )
|
|
|
|
ftsSearchPrevious->setEnabled( true );
|
|
|
|
} );
|
2022-02-27 05:17:37 +00:00
|
|
|
}
|
|
|
|
#else
|
2014-04-22 13:47:02 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
webview->findText( allMatches.at( ftsPosition ), flags, [ this ]( bool res ) {
|
|
|
|
ftsSearchNext->setEnabled( res );
|
|
|
|
if( !ftsSearchPrevious->isEnabled() )
|
|
|
|
ftsSearchPrevious->setEnabled( res );
|
|
|
|
} );
|
2014-04-22 13:47:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-27 05:17:37 +00:00
|
|
|
#endif
|
2022-07-02 13:36:19 +00:00
|
|
|
|
2023-03-17 09:42:44 +00:00
|
|
|
ftsSearchStatusLabel->setText( searchStatusMessage( ftsPosition + 1, allMatches.size() ) );
|
2014-04-22 13:47:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_ftsSearchPrevious_clicked()
|
|
|
|
{
|
|
|
|
performFtsFindOperation( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArticleView::on_ftsSearchNext_clicked()
|
|
|
|
{
|
|
|
|
performFtsFindOperation( false );
|
|
|
|
}
|
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
ResourceToSaveHandler::ResourceToSaveHandler(ArticleView * view, QString const & fileName ) :
|
2013-05-27 11:18:13 +00:00
|
|
|
QObject( view ),
|
2017-04-27 15:04:26 +00:00
|
|
|
fileName( fileName ),
|
2017-04-27 20:55:53 +00:00
|
|
|
alreadyDone( false )
|
2013-05-27 11:18:13 +00:00
|
|
|
{
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( this, &ResourceToSaveHandler::statusBarMessage, view, &ArticleView::statusBarMessage );
|
2017-04-27 20:55:53 +00:00
|
|
|
}
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
void ResourceToSaveHandler::addRequest( sptr< Dictionary::DataRequest > req )
|
2017-04-27 20:55:53 +00:00
|
|
|
{
|
|
|
|
if( !alreadyDone )
|
2013-05-27 11:18:13 +00:00
|
|
|
{
|
2017-04-27 20:55:53 +00:00
|
|
|
downloadRequests.push_back( req );
|
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( req.get(), &Dictionary::Request::finished, this, &ResourceToSaveHandler::downloadFinished );
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResourceToSaveHandler::downloadFinished()
|
|
|
|
{
|
2017-04-27 20:55:53 +00:00
|
|
|
if ( downloadRequests.empty() )
|
|
|
|
return; // Stray signal
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
// Find any finished resources
|
|
|
|
for( list< sptr< Dictionary::DataRequest > >::iterator i =
|
|
|
|
downloadRequests.begin(); i != downloadRequests.end(); )
|
2013-05-27 11:18:13 +00:00
|
|
|
{
|
2017-04-27 20:55:53 +00:00
|
|
|
if ( (*i)->isFinished() )
|
|
|
|
{
|
2022-11-01 09:40:20 +00:00
|
|
|
if ( (*i)->dataSize() >= 0 && !alreadyDone )
|
2017-04-27 20:55:53 +00:00
|
|
|
{
|
|
|
|
QByteArray resourceData;
|
|
|
|
vector< char > const & data = (*i)->getFullData();
|
|
|
|
resourceData = QByteArray( data.data(), data.size() );
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
// Write data to file
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
if ( !fileName.isEmpty() )
|
|
|
|
{
|
|
|
|
QFileInfo fileInfo( fileName );
|
|
|
|
QDir().mkpath( fileInfo.absoluteDir().absolutePath() );
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
QFile file( fileName );
|
|
|
|
if ( file.open( QFile::WriteOnly ) )
|
|
|
|
{
|
|
|
|
file.write( resourceData.data(), resourceData.size() );
|
|
|
|
file.close();
|
|
|
|
}
|
2013-05-27 11:18:13 +00:00
|
|
|
|
2017-04-27 20:55:53 +00:00
|
|
|
if ( file.error() )
|
|
|
|
{
|
|
|
|
emit statusBarMessage(
|
2022-01-27 00:23:57 +00:00
|
|
|
tr("ERROR: %1").arg(tr("Resource saving error: ") + file.errorString()),
|
|
|
|
10000, QPixmap(":/icons/error.svg"));
|
2017-04-27 20:55:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
alreadyDone = true;
|
|
|
|
|
|
|
|
// Clear other requests
|
|
|
|
|
|
|
|
downloadRequests.clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This one had no data. Erase it.
|
|
|
|
downloadRequests.erase( i++ );
|
|
|
|
}
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
2017-04-27 20:55:53 +00:00
|
|
|
else // Unfinished, wait.
|
|
|
|
break;
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
2017-04-27 20:55:53 +00:00
|
|
|
|
|
|
|
if ( downloadRequests.empty() )
|
2013-05-27 11:18:13 +00:00
|
|
|
{
|
2017-04-27 20:55:53 +00:00
|
|
|
if( !alreadyDone )
|
2017-04-27 15:04:26 +00:00
|
|
|
{
|
|
|
|
emit statusBarMessage(
|
2022-01-27 00:23:57 +00:00
|
|
|
tr("WARNING: %1").arg(tr("The referenced resource failed to download.")),
|
|
|
|
10000, QPixmap(":/icons/error.svg"));
|
2017-04-27 15:04:26 +00:00
|
|
|
}
|
2017-04-27 20:55:53 +00:00
|
|
|
emit done();
|
|
|
|
deleteLater();
|
2013-05-27 11:18:13 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-19 23:29:02 +00:00
|
|
|
|
2022-05-19 12:37:11 +00:00
|
|
|
ArticleViewAgent::ArticleViewAgent( ArticleView * articleView ) : QObject( articleView ), articleView( articleView )
|
2022-01-19 23:29:02 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-19 12:37:11 +00:00
|
|
|
void ArticleViewAgent::onJsActiveArticleChanged( QString const & id )
|
|
|
|
{
|
|
|
|
articleView->onJsActiveArticleChanged( id );
|
2022-01-19 23:29:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-19 12:37:11 +00:00
|
|
|
void ArticleViewAgent::linkClickedInHtml( QUrl const & url )
|
|
|
|
{
|
|
|
|
articleView->linkClickedInHtml( url );
|
2022-01-19 23:29:02 +00:00
|
|
|
}
|