diff --git a/about.cc b/about.cc index f11b3f8a..4aa03804 100644 --- a/about.cc +++ b/about.cc @@ -44,7 +44,7 @@ About::About( QWidget * parent ): QDialog( parent ) { QStringList creditsList = QString::fromUtf8( - creditsFile.readAll() ).split( '\n', QString::SkipEmptyParts ); + creditsFile.readAll() ).split( '\n', Qt::SkipEmptyParts ); QString html = ""; diff --git a/article_maker.cc b/article_maker.cc index 0cb85db7..159e169a 100644 --- a/article_maker.cc +++ b/article_maker.cc @@ -14,6 +14,7 @@ #include "langcoder.hh" #include "gddebug.hh" #include "utils.hh" +#include "globalbroadcaster.h" using std::vector; using std::string; @@ -55,6 +56,10 @@ std::string ArticleMaker::makeHtmlHeader( QString const & word, { result += ""; + + //custom javascript + result += ""; } // add qwebchannel @@ -630,6 +635,7 @@ void ArticleRequest::bodyFinished() bool wasUpdated = false; + QStringList dictIds; while ( bodyRequests.size() ) { // Since requests should go in order, check the first one first @@ -649,7 +655,7 @@ void ArticleRequest::bodyFinished() activeDicts[ activeDicts.size() - bodyRequests.size() ]; string dictId = activeDict->getId(); - + dictIds << QString::fromStdString(dictId); string head; string gdFrom = "gdfrom-" + Html::escape( dictId ); @@ -702,14 +708,6 @@ void ArticleRequest::bodyFinished() } string jsVal = Html::escapeForJavaScript( dictId ); - head += ""; head += string( "
emitDictIds(ActiveDictIds{word, dictIds}); + } + } else if (wasUpdated) { update(); + qDebug() << "send dicts(updated):" << dictIds; + emit GlobalBroadcaster::instance()->emitDictIds(ActiveDictIds{word, dictIds}); + } } void ArticleRequest::stemmedSearchFinished() diff --git a/article_netmgr.cc b/article_netmgr.cc index f38a7c85..cc8b8f40 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -207,10 +207,10 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, return QNetworkAccessManager::createRequest( op, newReq, outgoingData ); } - - QString contentType; - QUrl url=req.url(); + QMimeType mineType=db.mimeTypeForUrl (url); + QString contentType=mineType.name(); + if(req.url().scheme()=="gdlookup"){ QString path=url.path(); if(!path.isEmpty()){ @@ -379,7 +379,6 @@ sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( { if( url.scheme() == "gico" ) { - contentType="image/png"; QByteArray bytes; QBuffer buffer(&bytes); buffer.open(QIODevice::WriteOnly); @@ -485,8 +484,6 @@ qint64 ArticleResourceReply::readData( char * out, qint64 maxSize ) if ( maxSize == 0 ) return 0; - GD_DPRINTF( "====reading %d bytes\n", (int)maxSize ); - bool finished = req->isFinished(); qint64 avail = req->dataSize(); @@ -497,6 +494,7 @@ qint64 ArticleResourceReply::readData( char * out, qint64 maxSize ) qint64 left = avail - alreadyRead; qint64 toRead = maxSize < left ? maxSize : left; + GD_DPRINTF( "====reading %d bytes\n", (int)toRead ); try { @@ -517,13 +515,15 @@ qint64 ArticleResourceReply::readData( char * out, qint64 maxSize ) void ArticleResourceReply::readyReadSlot() { - readyRead(); + emit readyRead(); } void ArticleResourceReply::finishedSlot() { - if ( req->dataSize() < 0 ) - error( ContentNotFoundError ); + if (req->dataSize() < 0) { + emit error(ContentNotFoundError); + setError(ContentNotFoundError, "content not found"); + } emit finished(); } diff --git a/article_netmgr.hh b/article_netmgr.hh index 61cdfc47..ea903b27 100644 --- a/article_netmgr.hh +++ b/article_netmgr.hh @@ -99,6 +99,7 @@ class ArticleNetworkAccessManager: public QNetworkAccessManager ArticleMaker const & articleMaker; bool const & disallowContentFromOtherSites; bool const & hideGoldenDictHeader; + QMimeDatabase db; public: diff --git a/articleview.cc b/articleview.cc index a6a8fa34..2541643b 100644 --- a/articleview.cc +++ b/articleview.cc @@ -28,7 +28,7 @@ #include #include #include - +#include #ifdef Q_OS_WIN32 #include #include @@ -40,6 +40,7 @@ #include "speechclient.hh" #endif +#include "globalbroadcaster.h" using std::map; using std::list; @@ -180,7 +181,7 @@ QString ArticleView::runJavaScriptSync(QWebEnginePage* frame, const QString& var QString result; QSharedPointer loop = QSharedPointer(new QEventLoop()); QTimer::singleShot(1000, loop.data(), &QEventLoop::quit); - frame->runJavaScript(variable, [loop,&result](const QVariant &v) + frame->runJavaScript(variable, [=,&result](const QVariant &v) { if(loop->isRunning()){ if(v.isValid()) @@ -275,8 +276,6 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, connect(ui.definition, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); - attachToJavaScript(); - connect( ui.definition->page(), SIGNAL( titleChanged( QString const & ) ), this, SLOT( handleTitleChanged( QString const & ) ) ); @@ -340,6 +339,7 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, QWebEngineSettings * settings = ui.definition->page()->settings(); settings->defaultSettings()->setAttribute( QWebEngineSettings::WebAttribute::LocalContentCanAccessRemoteUrls, true ); settings->defaultSettings()->setAttribute( QWebEngineSettings::WebAttribute::LocalContentCanAccessFileUrls, true ); + settings->defaultSettings()->setAttribute( QWebEngineSettings::WebAttribute::ErrorPageEnabled, false); // Load the default blank page instantly, so there would be no flicker. QString contentType; @@ -359,6 +359,12 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, // Variable name for store current selection range rangeVarName = QString( "sr_%1" ).arg( QString::number( (quint64)this, 16 ) ); + + connect(GlobalBroadcaster::instance(), SIGNAL( emitDictIds(ActiveDictIds)), this, + SLOT(setActiveDictIds(ActiveDictIds))); + + channel = new QWebChannel(ui.definition->page()); + attachToJavaScript(); } // explicitly report the minimum size, to avoid @@ -377,7 +383,7 @@ ArticleView::~ArticleView() { cleanupTemp(); audioPlayer->stop(); - + channel->deregisterObject(this); ui.definition->ungrabGesture( Gestures::GDPinchGestureType ); ui.definition->ungrabGesture( Gestures::GDSwipeGestureType ); } @@ -386,6 +392,8 @@ void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned g QString const & scrollTo, Contexts const & contexts_ ) { + currentWord = phrase.phrase; + currentActiveDictIds.clear(); // first, let's stop the player audioPlayer->stop(); @@ -508,15 +516,14 @@ void ArticleView::showAnticipation() void ArticleView::loadFinished( bool ) { - QUrl url = ui.definition->url(); - QObject* obj=sender(); - qDebug()<<"article view loaded url:"<url(); + qDebug() << "article view loaded url:" << url; QVariant userDataVariant = ui.definition->property("currentArticle"); if ( userDataVariant.isValid() ) { - QString currentArticle = userDataVariant.toString(); if ( !currentArticle.isEmpty() ) @@ -645,8 +652,7 @@ unsigned ArticleView::getGroup( QUrl const & url ) QStringList ArticleView::getArticlesList() { - return runJavaScriptSync( ui.definition->page(), "gdArticleContents" ) - .trimmed().split( ' ', Qt::SkipEmptyParts ); + return currentActiveDictIds; } QString ArticleView::getActiveArticleId() @@ -758,7 +764,7 @@ void ArticleView::tryMangleWebsiteClickedUrl( QUrl & url, Contexts & contexts ) void ArticleView::updateCurrentArticleFromCurrentFrame( QWebEnginePage * frame ,QPoint * point) { - qDebug("updateCurrentArticleFromCurrentFrame"); + } void ArticleView::saveHistoryUserData() @@ -916,7 +922,8 @@ bool ArticleView::eventFilter( QObject * obj, QEvent * ev ) keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab || keyEvent->key() == Qt::Key_Return || - keyEvent->key() == Qt::Key_Enter ) + keyEvent->key() == Qt::Key_Enter || + keyEvent->key() == Qt::Key_Escape) return false; // Those key have other uses than to start typing QString text = keyEvent->text(); @@ -972,41 +979,36 @@ QString ArticleView::getMutedForGroup( unsigned group ) return QString(); } -QStringList ArticleView::getMutedDictionaries(unsigned group) -{ - if (dictionaryBarToggled && dictionaryBarToggled->isChecked()) - { - // Dictionary bar is active -- mute the muted dictionaries - Instances::Group const* groupInstance = groups.findGroup(group); +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(); + // 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; + QStringList mutedDicts; - if (groupInstance) - { - for (unsigned x = 0; x < groupInstance->dictionaries.size(); ++x) - { - QString id = QString::fromStdString( - groupInstance->dictionaries[x]->getId()); + 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); - } - } + if (mutedDictionaries->contains(id)) + mutedDicts.append(id); + } + } - return mutedDicts; - } + return mutedDicts; + } - return QStringList(); + return QStringList(); } void ArticleView::linkHovered ( const QString & link ) @@ -1084,7 +1086,7 @@ void ArticleView::linkHovered ( const QString & link ) } void ArticleView::attachToJavaScript() { - QWebChannel *channel = new QWebChannel(ui.definition->page()); + // set the web channel to be used by the page // see http://doc.qt.io/qt-5/qwebenginepage.html#setWebChannel @@ -1124,7 +1126,7 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const & scrollTo, Contexts const & contexts_ ) { - qDebug() << "clicked" << url; + qDebug() << "open link url:" << url; Contexts contexts( contexts_ ); @@ -1716,13 +1718,14 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) QAction * saveImageAction = 0; QAction * saveSoundAction = 0; - //todo url() or lastclickurl ? - QUrl targetUrl( r->url() ); + QWebEngineContextMenuData menuData=r->contextMenuData(); + QUrl targetUrl(menuData.linkUrl()); + qDebug() << "menu:" << menuData.linkText()<<":"<url().isEmpty() ) + if ( !targetUrl.isEmpty() ) { if ( !isExternalLink( targetUrl ) ) { @@ -1737,7 +1740,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) } } - if ( isExternalLink( r->url() ) ) + if ( isExternalLink( targetUrl ) ) { followLinkExternal = new QAction( tr( "Open Link in &External Browser" ), &menu ); menu.addAction( followLinkExternal ); @@ -1745,6 +1748,25 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) } } + QUrl imageUrl; + if( !popupView && menuData.mediaType ()==QWebEngineContextMenuData::MediaTypeImage) + { + imageUrl = menuData.mediaUrl (); + if( !imageUrl.isEmpty() ) + { + menu.addAction( ui.definition->pageAction( QWebEnginePage::CopyImageToClipboard ) ); + saveImageAction = new QAction( tr( "Save &image..." ), &menu ); + menu.addAction( saveImageAction ); + } + } + + if( !popupView && ( targetUrl.scheme() == "gdau" + || Dictionary::WebMultimediaDownload::isAudioUrl( targetUrl ) ) ) + { + saveSoundAction = new QAction( tr( "Save s&ound..." ), &menu ); + menu.addAction( saveSoundAction ); + } + QString selectedText = ui.definition->selectedText(); QString text = selectedText.trimmed(); @@ -1896,7 +1918,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) openLink( targetUrl, ui.definition->url(), getCurrentArticle(), contexts ); else if ( result == followLinkExternal ) - QDesktopServices::openUrl( r->url() ); + QDesktopServices::openUrl( targetUrl ); else if ( result == lookupSelection ) showDefinition( selectedText, getGroup( ui.definition->url() ), getCurrentArticle() ); @@ -1988,7 +2010,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) } } - qDebug( "url = %s\n", r->url().toString().toLocal8Bit().data() ); + qDebug( "url = %s\n", targetUrl.toString().toLocal8Bit().data() ); qDebug( "title = %s\n", r->title().toLocal8Bit().data() ); } @@ -2526,6 +2548,16 @@ QString ArticleView::getWebPageTextSync(QWebEnginePage * page){ return planText; } +void ArticleView::setActiveDictIds(ActiveDictIds ad) { + // ignore all other signals. + + if (ad.word == currentWord) { + currentActiveDictIds << ad.dictIds; + currentActiveDictIds.removeDuplicates(); + qDebug() << "current word:"< desktopOpenedTempFiles; QAction * dictionaryBarToggled; - GroupComboBox const * groupComboBox; + GroupComboBox const *groupComboBox; + + /// current searching word. + QString currentWord; + + /// current active dict id list; + QStringList currentActiveDictIds; /// Search in results of full-text search QStringList allMatches; @@ -276,14 +283,14 @@ public slots: /// Selects an entire text of the current article void selectCurrentArticle(); - + void linkClicked( QUrl const & ); private slots: void loadFinished( bool ok ); void handleTitleChanged( QString const & title ); void handleUrlChanged( QUrl const & url ); void attachToJavaScript(); - void linkClicked( QUrl const & ); + void linkHovered( const QString & link); void contextMenuRequested( QPoint const & ); @@ -322,13 +329,14 @@ private slots: /// Inspect element void inspect(); + void setActiveDictIds(ActiveDictIds); + private: /// Deduces group from the url. If there doesn't seem to be any group, /// returns 0. unsigned getGroup( QUrl const & ); - /// Returns current article in the view, in the form of "gdfrom-xxx" id. QString getCurrentArticle(); @@ -374,6 +382,7 @@ private: QStringList getMutedDictionaries(unsigned group); + protected: // We need this to hide the search bar when we're showed void showEvent( QShowEvent * ); diff --git a/articlewebview.cc b/articlewebview.cc index 68252527..1d412386 100644 --- a/articlewebview.cc +++ b/articlewebview.cc @@ -64,7 +64,7 @@ bool ArticleWebView::eventFilter(QObject *obj, QEvent *ev) mouseReleaseEvent(pe); if (firstClicked) { QTimer::singleShot(QApplication::doubleClickInterval(),this,[=](){ - singleClickAction(pe); + singleClickAction(obj,pe); }); } else { doubleClickAction(pe); @@ -73,12 +73,10 @@ bool ArticleWebView::eventFilter(QObject *obj, QEvent *ev) if (ev->type() == QEvent::Wheel) { QWheelEvent *pe = static_cast(ev); wheelEvent(pe); - //return true; } if (ev->type() == QEvent::FocusIn) { QFocusEvent *pe = static_cast(ev); focusInEvent(pe); - //return true; } return QWebEngineView::eventFilter(obj, ev); @@ -92,36 +90,31 @@ void ArticleWebView::mousePressEvent(QMouseEvent *event) //QWebEngineView::mousePressEvent(event); } -void ArticleWebView::singleClickAction( QMouseEvent * event ) +void ArticleWebView::singleClickAction(QObject* obj, QMouseEvent * event ) { if(!firstClicked) return; if (selectionBySingleClick) { - // findText(""); // clear the selection first, if any - page()->runJavaScript(QString( - " var s = window.getSelection(); " - " if(s.rangeCount>0){ " - " var range = s.getRangeAt(0); " - " var node = s.anchorNode; " - " while (range.toString().indexOf(' ') != 0) { " - " range.setStart(node, (range.startOffset - 1)); " - " } " - " range.setStart(node, range.startOffset + 1); " - " do { " - " range.setEnd(node, range.endOffset+1); " - " } " - " while (range.toString().indexOf(' ') == -1 && range.toString().trim() != ''); " - " range.setEnd(node,range.endOffset-1);" - " var str = range.toString().trim(); " - " console.log(str);" - " }")); + findText(""); // clear the selection first, if any + //send dbl click event twice? send one time seems not work .weird really. need further investigate. + sendCustomMouseEvent(obj, QEvent::MouseButtonDblClick); + sendCustomMouseEvent(obj, QEvent::MouseButtonDblClick); } } +void ArticleWebView::sendCustomMouseEvent(QObject *obj, QEvent::Type type) { + QPoint pt = mapFromGlobal(QCursor::pos()); + QMouseEvent ev(type, pt, pt, QCursor::pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier, + Qt::MouseEventSynthesizedByApplication); -void ArticleWebView::mouseReleaseEvent( QMouseEvent * event ) -{ + QObjectList list = this->children(); + for (int i = 0; i < list.size(); i++) { + QApplication::sendEvent(list[i], &ev); + } +} + +void ArticleWebView::mouseReleaseEvent(QMouseEvent *event) { bool noMidButton = !( event->buttons() & Qt::MiddleButton ); //QWebEngineView::mouseReleaseEvent( event ); @@ -130,18 +123,13 @@ void ArticleWebView::mouseReleaseEvent( QMouseEvent * event ) midButtonPressed = false; } -void ArticleWebView::doubleClickAction( QMouseEvent * event ) -{ - //QWebEngineView::mouseDoubleClickEvent( event ); +void ArticleWebView::doubleClickAction(QMouseEvent *event) { + // QWebEngineView::mouseDoubleClickEvent( event ); - int scrollBarWidth = 0; - int scrollBarHeight = 0; - - // emit the signal only if we are not double-clicking on scrollbars - if ((event->x() < width() - scrollBarWidth) && (event->y() < height() - scrollBarHeight)) { - emit doubleClicked(event->pos()); + // emit the signal only if we are not double-clicking on scrollbars + if (Qt::MouseEventSynthesizedByApplication != event->source()) { + emit doubleClicked(event->pos()); } - } void ArticleWebView::focusInEvent( QFocusEvent * event ) diff --git a/articlewebview.hh b/articlewebview.hh index 7099e463..14decd16 100644 --- a/articlewebview.hh +++ b/articlewebview.hh @@ -51,7 +51,8 @@ public: protected: bool event( QEvent * event ); - void singleClickAction( QMouseEvent * event ); + void singleClickAction(QObject *obj, QMouseEvent *event); + void sendCustomMouseEvent(QObject *obj, QEvent::Type type); void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent( QMouseEvent * event ); void doubleClickAction( QMouseEvent * event ); diff --git a/dictserver.cc b/dictserver.cc index bfd0d085..151c32df 100644 --- a/dictserver.cc +++ b/dictserver.cc @@ -200,11 +200,11 @@ public: if( pos < 0 ) url = "dict://" + url; - databases = database_.split( QRegExp( "[ ,;]" ), QString::SkipEmptyParts ); + databases = database_.split( QRegExp( "[ ,;]" ), Qt::SkipEmptyParts ); if( databases.isEmpty() ) databases.append( "*" ); - strategies = strategies_.split( QRegExp( "[ ,;]" ), QString::SkipEmptyParts ); + strategies = strategies_.split( QRegExp( "[ ,;]" ), Qt::SkipEmptyParts ); if( strategies.isEmpty() ) strategies.append( "prefix" ); } diff --git a/epwing_book.cc b/epwing_book.cc index 11cf7386..18f38298 100644 --- a/epwing_book.cc +++ b/epwing_book.cc @@ -555,7 +555,7 @@ bool EpwingBook::setSubBook( int book_nom ) QString line = ts.readLine(); while( !line.isEmpty() ) { - QStringList list = line.remove( '\n' ).split( ' ', QString::SkipEmptyParts ); + QStringList list = line.remove( '\n' ).split( ' ', Qt::SkipEmptyParts ); if( list.count() == 2 ) customFontsMap[ list[ 0 ] ] = list[ 1 ]; line = ts.readLine(); diff --git a/favoritespanewidget.cc b/favoritespanewidget.cc index 9a00d1c1..0ac47c34 100644 --- a/favoritespanewidget.cc +++ b/favoritespanewidget.cc @@ -976,7 +976,7 @@ bool FavoritesModel::addNewHeadword( const QString & path, const QString & headw // Find or create target folder - QStringList folders = path.split( "/", QString::SkipEmptyParts ); + QStringList folders = path.split( "/", Qt::SkipEmptyParts ); QStringList::const_iterator it = folders.begin(); for( ; it != folders.end(); ++it ) parentIdx = forceFolder( *it, parentIdx ); @@ -992,7 +992,7 @@ bool FavoritesModel::removeHeadword( const QString & path, const QString & headw // Find target folder - QStringList folders = path.split( "/", QString::SkipEmptyParts ); + QStringList folders = path.split( "/", Qt::SkipEmptyParts ); QStringList::const_iterator it = folders.begin(); for( ; it != folders.end(); ++it ) { @@ -1022,7 +1022,7 @@ bool FavoritesModel::isHeadwordPresent( const QString & path, const QString & he // Find target folder - QStringList folders = path.split( "/", QString::SkipEmptyParts ); + QStringList folders = path.split( "/", Qt::SkipEmptyParts ); QStringList::const_iterator it = folders.begin(); for( ; it != folders.end(); ++it ) { diff --git a/forvo.cc b/forvo.cc index b9ca291c..c225990d 100644 --- a/forvo.cc +++ b/forvo.cc @@ -353,7 +353,7 @@ vector< sptr< Dictionary::Class > > makeDictionaries( if ( forvo.enable ) { - QStringList codes = forvo.languageCodes.split( ',', QString::SkipEmptyParts ); + QStringList codes = forvo.languageCodes.split( ',', Qt::SkipEmptyParts ); QSet< QString > usedCodes; diff --git a/ftshelpers.cc b/ftshelpers.cc index 12e763b8..d3920b64 100644 --- a/ftshelpers.cc +++ b/ftshelpers.cc @@ -91,17 +91,17 @@ bool parseSearchString( QString const & str, QStringList & indexWords, // Make words list for search in article text searchWords = str.normalized( QString::NormalizationForm_C ) - .split( spacesRegExp, QString::SkipEmptyParts ); + .split( spacesRegExp, Qt::SkipEmptyParts ); // Make words list for index search QStringList list = str.normalized( QString::NormalizationForm_C ) - .toLower().split( spacesRegExp, QString::SkipEmptyParts ); + .toLower().split( spacesRegExp, Qt::SkipEmptyParts ); indexWords = list.filter( wordRegExp ); indexWords.removeDuplicates(); // Make regexp for results hilite - QStringList allWords = str.split( spacesRegExp, QString::SkipEmptyParts ); + QStringList allWords = str.split( spacesRegExp, Qt::SkipEmptyParts ); QString searchString = makeHiliteRegExpString( allWords, searchMode, distanceBetweenWords ); searchRegExp = QRegExp( searchString, matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive, @@ -124,7 +124,7 @@ bool parseSearchString( QString const & str, QStringList & indexWords, tmp.replace( setsRegExp, " " ); QStringList list = tmp.normalized( QString::NormalizationForm_C ) - .toLower().split( spacesRegExp, QString::SkipEmptyParts ); + .toLower().split( spacesRegExp, Qt::SkipEmptyParts ); if( hasCJK ) { @@ -189,7 +189,7 @@ void parseArticleForFts( uint32_t articleAddress, QString & articleText, QStringList articleWords = articleText.normalized( QString::NormalizationForm_C ) .split( QRegularExpression( handleRoundBrackets ? "[^\\w\\(\\)\\p{M}]+" : "[^\\w\\p{M}]+", QRegularExpression::UseUnicodePropertiesOption ), - QString::SkipEmptyParts ); + Qt::SkipEmptyParts ); QSet< QString > setOfWords; @@ -233,7 +233,7 @@ void parseArticleForFts( uint32_t articleAddress, QString & articleText, // Special handle for words with round brackets - DSL feature QStringList list; - QStringList oldVariant = word.split( regSplit, QString::SkipEmptyParts ); + QStringList oldVariant = word.split( regSplit, Qt::SkipEmptyParts ); for( QStringList::iterator it = oldVariant.begin(); it != oldVariant.end(); ++it ) if( it->size() >= FTS::MinimumWordSize && !list.contains( *it ) ) list.append( *it ); @@ -509,7 +509,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, articleText = gd::toQString( Folding::applyDiacriticsOnly( gd::toWString( articleText ) ) ); QStringList articleWords = articleText.split( needHandleBrackets ? splitWithBrackets : splitWithoutBrackets, - QString::SkipEmptyParts ); + Qt::SkipEmptyParts ); int wordsNum = articleWords.length(); while ( pos < wordsNum ) @@ -540,7 +540,7 @@ void FTSResultsRequest::checkArticles( QVector< uint32_t > const & offsets, parsedWords.append( word ); } else - parsedWords = s.split( regSplit, QString::SkipEmptyParts ); + parsedWords = s.split( regSplit, Qt::SkipEmptyParts ); } else parsedWords.append( s ); diff --git a/gico_schemahandler.cpp b/gico_schemahandler.cpp index ae6c5ecb..2cc97a4c 100644 --- a/gico_schemahandler.cpp +++ b/gico_schemahandler.cpp @@ -10,10 +10,17 @@ void GicoSchemeHandler::requestStarted(QWebEngineUrlRequestJob *requestJob) QNetworkRequest request; request.setUrl( url ); - QNetworkReply* reply=this->mManager.createRequest(QNetworkAccessManager::GetOperation,request,NULL); + QNetworkReply *reply = this->mManager.createRequest(QNetworkAccessManager::GetOperation, request, NULL); - QMimeType mineType=db.mimeTypeForUrl (url); - QString contentType=mineType.name (); - // Reply segment - requestJob->reply(contentType.toLatin1(), reply); + connect(reply, &QNetworkReply::finished, requestJob, [=]() { + if (reply->error() == QNetworkReply::ContentNotFoundError) { + requestJob->fail(QWebEngineUrlRequestJob::UrlNotFound); + return; + } + + QMimeType mineType = db.mimeTypeForUrl(url); + QString contentType = mineType.name(); + // Reply segment + requestJob->reply(contentType.toLatin1(), reply); + }); } diff --git a/globalbroadcaster.cpp b/globalbroadcaster.cpp new file mode 100644 index 00000000..02369842 --- /dev/null +++ b/globalbroadcaster.cpp @@ -0,0 +1,14 @@ +#include "globalbroadcaster.h" +#include + + +Q_GLOBAL_STATIC(GlobalBroadcaster, bdcaster) +GlobalBroadcaster::GlobalBroadcaster(QObject *parent) : QObject(parent) +{ + +} + + +GlobalBroadcaster* GlobalBroadcaster::instance() { return bdcaster; } + +// namespace global diff --git a/globalbroadcaster.h b/globalbroadcaster.h new file mode 100644 index 00000000..912d5c88 --- /dev/null +++ b/globalbroadcaster.h @@ -0,0 +1,24 @@ +#ifndef GLOBAL_GLOBALBROADCASTER_H +#define GLOBAL_GLOBALBROADCASTER_H + +#include + +struct ActiveDictIds { + QString word; + QStringList dictIds; +} +; + +class GlobalBroadcaster : public QObject +{ + Q_OBJECT +public: + GlobalBroadcaster(QObject *parent = nullptr); + static GlobalBroadcaster *instance(); +signals: + void emitDictIds(ActiveDictIds ad); + +}; + + +#endif // GLOBAL_GLOBALBROADCASTER_H diff --git a/goldendict.pro b/goldendict.pro index b999f9e5..6782f3d4 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -250,6 +250,7 @@ DEFINES += PROGRAM_VERSION=\\\"$$VERSION\\\" # Input HEADERS += folding.hh \ gico_schemahandler.h \ + globalbroadcaster.h \ inc_case_folding.hh \ inc_diacritic_folding.hh \ mainwindow.hh \ @@ -390,6 +391,7 @@ FORMS += groups.ui \ SOURCES += folding.cc \ gico_schemahandler.cpp \ + globalbroadcaster.cpp \ main.cc \ dictionary.cc \ config.cc \ diff --git a/main.cc b/main.cc index 26c38bff..3d866052 100644 --- a/main.cc +++ b/main.cc @@ -263,8 +263,7 @@ int main( int argc, char ** argv ) #endif - - QStringList localSchemes={"gdlookup","gdau","gico","qrcx","bres","bword"}; + QStringList localSchemes={"gdlookup","gdau","gico","qrcx","bres","bword","gdprg","gdvideo","gdpicture","gdtts"}; for (int i = 0; i < localSchemes.size(); ++i) { @@ -272,11 +271,11 @@ int main( int argc, char ** argv ) QWebEngineUrlScheme webUiScheme(localScheme.toLatin1()); webUiScheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::LocalScheme | - QWebEngineUrlScheme::LocalAccessAllowed); + QWebEngineUrlScheme::LocalAccessAllowed| + QWebEngineUrlScheme::CorsEnabled); QWebEngineUrlScheme::registerScheme(webUiScheme); } - QHotkeyApplication app( "GoldenDict", argc, argv ); LogFilePtrGuard logFilePtrGuard; diff --git a/mainwindow.cc b/mainwindow.cc index 002f6bdf..31a41cf7 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -150,7 +150,7 @@ MainWindow::MainWindow( Config::Class & cfg_ ): QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("gdlookup", handler); QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("bword", handler); - QStringList localSchemes={"gdau","gico","qrcx","bres"}; + QStringList localSchemes={"gdau","gico","qrcx","bres","gdprg","gdvideo","gdpicture","gdtts"}; GicoSchemeHandler *h=new GicoSchemeHandler(articleNetMgr); for(int i=0;iinstallUrlSchemeHandler(localSchemes.at(i).toLatin1(), h); @@ -1219,12 +1219,21 @@ void MainWindow::closeEvent( QCloseEvent * ev ) { if ( cfg.preferences.enableTrayIcon && cfg.preferences.closeToTray ) { - ev->ignore(); - if( !cfg.preferences.searchInDock ) translateBox->setPopupEnabled( false ); +#ifdef HAVE_X11 + // Don't ignore the close event, because doing so cancels session logout if + // the main window is visible when the user attempts to log out. + // The main window will be only hidden, because QApplication::quitOnLastWindowClosed + // property is false and Qt::WA_DeleteOnClose widget attribute is not set. + Q_ASSERT(!QApplication::quitOnLastWindowClosed()); + Q_ASSERT(!testAttribute(Qt::WA_DeleteOnClose)); +#else + // Ignore the close event because closing the main window breaks global hotkeys on Windows. + ev->ignore(); hide(); +#endif } else { @@ -1293,11 +1302,12 @@ void MainWindow::applyProxySettings() void MainWindow::applyWebSettings() { - QWebEngineSettings *defaultSettings = QWebEngineSettings::globalSettings(); + QWebEngineSettings *defaultSettings = QWebEngineSettings::defaultSettings(); defaultSettings->setAttribute(QWebEngineSettings::PluginsEnabled, cfg.preferences.enableWebPlugins); defaultSettings->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, false); defaultSettings->setAttribute( QWebEngineSettings::WebAttribute::LocalContentCanAccessRemoteUrls, true ); defaultSettings->setAttribute( QWebEngineSettings::WebAttribute::LocalContentCanAccessFileUrls, true ); + defaultSettings->setAttribute( QWebEngineSettings::WebAttribute::ErrorPageEnabled, false); } void MainWindow::setupNetworkCache( int maxSize ) @@ -1672,7 +1682,12 @@ ArticleView * MainWindow::createNewTab( bool switchToIt, connect( view, SIGNAL( zoomIn()), this, SLOT( zoomin() ) ); connect( view, SIGNAL( zoomOut()), this, SLOT( zoomout() ) ); - connect (wuri,SIGNAL(linkClicked(QUrl)),view,SLOT(linkClicked(QUrl))); + connect(wuri, &WebUrlRequestInterceptor::linkClicked, view, [=](QUrl url) { + ArticleView *active = getCurrentArticleView(); + if (active == view) { + view->linkClicked(url); + } + }); view->setSelectionBySingleClick( cfg.preferences.selectWordBySingleClick ); @@ -2619,7 +2634,8 @@ bool MainWindow::eventFilter( QObject * obj, QEvent * ev ) if ( keyEvent->key() == Qt::Key_Space || keyEvent->key() == Qt::Key_Backspace || keyEvent->key() == Qt::Key_Tab || - keyEvent->key() == Qt::Key_Backtab ) + keyEvent->key() == Qt::Key_Backtab || + keyEvent->key() == Qt::Key_Escape) return false; // Those key have other uses than to start typing // or don't make sense @@ -2648,7 +2664,8 @@ bool MainWindow::eventFilter( QObject * obj, QEvent * ev ) if ( keyEvent->key() == Qt::Key_Space || keyEvent->key() == Qt::Key_Backspace || keyEvent->key() == Qt::Key_Tab || - keyEvent->key() == Qt::Key_Backtab ) + keyEvent->key() == Qt::Key_Backtab || + keyEvent->key() == Qt::Key_Escape) return false; // Those key have other uses than to start typing // or don't make sense diff --git a/programs.cc b/programs.cc index e4e9425d..3b8c7aef 100644 --- a/programs.cc +++ b/programs.cc @@ -353,7 +353,7 @@ void ProgramWordSearchRequest::instanceFinished( QByteArray output, QString erro // Handle any Windows artifacts output.replace( "\r\n", "\n" ); QStringList result = - QString::fromUtf8( output ).split( "\n", QString::SkipEmptyParts ); + QString::fromUtf8( output ).split( "\n", Qt::SkipEmptyParts ); for( int x = 0; x < result.size(); ++x ) matches.push_back( Dictionary::WordMatch( gd::toWString( result[ x ] ) ) ); diff --git a/resources.qrc b/resources.qrc index 9ab92cae..29f3431d 100644 --- a/resources.qrc +++ b/resources.qrc @@ -85,5 +85,6 @@ icons/folder.png icons/ontop.png resources/jquery-3.6.0.slim.min.js + resources/gd_custom.js diff --git a/resources/gd_custom.js b/resources/gd_custom.js new file mode 100644 index 00000000..049aa35b --- /dev/null +++ b/resources/gd_custom.js @@ -0,0 +1,27 @@ +//document ready , +$(function() { + $("a").click(function(event) { + var link = $(this).attr("href"); + if(link.indexOf(":")>=0){ + return; + } + + var newLink; + if (link.startsWith("#")) { + newLink = window.location.href + link; + } else { + var href = window.location.href; + var index = href.indexOf("?"); + newLink = href.substring(0, index) + "?word=" + link; + } + $(this).attr("href", newLink); + + }); + + } + +); +function playSound(sound) { + var a = new Audio(sound); + a.play(); + } diff --git a/stardict.cc b/stardict.cc index 19452ecd..eeb62d9f 100644 --- a/stardict.cc +++ b/stardict.cc @@ -635,7 +635,7 @@ void StardictDictionary::pangoToHtml( QString & text ) { // Parse font description - QStringList list = styleRegex.cap( 2 ).split( " ", QString::SkipEmptyParts ); + QStringList list = styleRegex.cap( 2 ).split( " ", Qt::SkipEmptyParts ); int n; QString sizeStr, stylesStr, familiesStr; for( n = list.size() - 1; n >= 0; n-- ) diff --git a/utils.hh b/utils.hh index c8b57596..a8370104 100644 --- a/utils.hh +++ b/utils.hh @@ -26,6 +26,13 @@ inline QString rstrip(const QString& str) { return ""; } + inline bool isExternalLink( QUrl const & url ) + { + return url.scheme() == "http" || url.scheme() == "https" || + url.scheme() == "ftp" || url.scheme() == "mailto" || + url.scheme() == "file"; + } + inline QString escape( QString const & plain ) { return plain.toHtmlEscaped(); diff --git a/weburlrequestinterceptor.cpp b/weburlrequestinterceptor.cpp index 6afec740..d0af0b5b 100644 --- a/weburlrequestinterceptor.cpp +++ b/weburlrequestinterceptor.cpp @@ -1,13 +1,16 @@ #include "weburlrequestinterceptor.h" #include +#include "utils.hh" WebUrlRequestInterceptor::WebUrlRequestInterceptor(QObject *p) :QWebEngineUrlRequestInterceptor(p) { } -void WebUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { - if(QWebEngineUrlRequestInfo::NavigationTypeLink==info.navigationType ()&&info.resourceType ()==QWebEngineUrlRequestInfo::ResourceTypeMainFrame) - emit linkClicked(info.requestUrl ()); +void WebUrlRequestInterceptor::interceptRequest( QWebEngineUrlRequestInfo &info) { + if (QWebEngineUrlRequestInfo::NavigationTypeLink == info.navigationType() && info.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) { + emit linkClicked(info.requestUrl()); + info.block(true); + } } diff --git a/winlibs/lib/avcodec-gd-58.dll b/winlibs/lib/avcodec-gd-58.dll index 19024f89..3a3caf04 100644 Binary files a/winlibs/lib/avcodec-gd-58.dll and b/winlibs/lib/avcodec-gd-58.dll differ diff --git a/winlibs/lib/avformat-gd-58.dll b/winlibs/lib/avformat-gd-58.dll index 3c718941..7cf88d3c 100644 Binary files a/winlibs/lib/avformat-gd-58.dll and b/winlibs/lib/avformat-gd-58.dll differ diff --git a/winlibs/lib/avutil-gd-56.dll b/winlibs/lib/avutil-gd-56.dll index 8aceea05..57c1805d 100644 Binary files a/winlibs/lib/avutil-gd-56.dll and b/winlibs/lib/avutil-gd-56.dll differ diff --git a/winlibs/lib/libavcodec-gd.dll.a b/winlibs/lib/libavcodec-gd.dll.a index 8bcbe025..22e7b089 100644 Binary files a/winlibs/lib/libavcodec-gd.dll.a and b/winlibs/lib/libavcodec-gd.dll.a differ diff --git a/winlibs/lib/libavformat-gd.dll.a b/winlibs/lib/libavformat-gd.dll.a index cd9d4b79..07bf1dca 100644 Binary files a/winlibs/lib/libavformat-gd.dll.a and b/winlibs/lib/libavformat-gd.dll.a differ diff --git a/winlibs/lib/libavutil-gd.dll.a b/winlibs/lib/libavutil-gd.dll.a index 3ee964b2..5fd97637 100644 Binary files a/winlibs/lib/libavutil-gd.dll.a and b/winlibs/lib/libavutil-gd.dll.a differ diff --git a/winlibs/lib/libswresample-gd.dll.a b/winlibs/lib/libswresample-gd.dll.a index 08fdef91..c49c0edc 100644 Binary files a/winlibs/lib/libswresample-gd.dll.a and b/winlibs/lib/libswresample-gd.dll.a differ diff --git a/winlibs/lib/swresample-gd-3.dll b/winlibs/lib/swresample-gd-3.dll index 904bb248..a4660ef8 100644 Binary files a/winlibs/lib/swresample-gd-3.dll and b/winlibs/lib/swresample-gd-3.dll differ diff --git a/zim.cc b/zim.cc index 52dd04ee..a75097d5 100644 --- a/zim.cc +++ b/zim.cc @@ -1188,7 +1188,7 @@ public: ~ZimArticleRequest() { isCancelled.ref(); - hasExited.acquire(); + //hasExited.acquire(); } }; @@ -1353,7 +1353,7 @@ public: ~ZimResourceRequestRunnable() { - hasExited.release(); + //hasExited.release(); } virtual void run(); @@ -1371,14 +1371,11 @@ class ZimResourceRequest: public Dictionary::DataRequest QSemaphore hasExited; public: - - ZimResourceRequest( ZimDictionary & dict_, - string const & resourceName_ ): - dict( dict_ ), - resourceName( resourceName_ ) - { - QThreadPool::globalInstance()->start( - new ZimResourceRequestRunnable( *this, hasExited ) ); + ZimResourceRequest(ZimDictionary &dict_, string const &resourceName_) + : dict(dict_), resourceName(resourceName_) { + //(new ZimResourceRequestRunnable(*this, hasExited))->run(); + QThreadPool::globalInstance()->start( + new ZimResourceRequestRunnable( *this, hasExited ) ); } void run(); // Run from another thread by ZimResourceRequestRunnable @@ -1391,7 +1388,7 @@ public: ~ZimResourceRequest() { isCancelled.ref(); - hasExited.acquire(); + //hasExited.acquire(); } };