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 "article_maker.hh"
# include "config.hh"
2023-04-18 00:41:47 +00:00
# include "folding.hh"
# include "gddebug.hh"
# include "globalbroadcaster.hh"
# include "globalregex.hh"
2009-01-28 20:55:45 +00:00
# include "htmlescape.hh"
2023-04-18 00:41:47 +00:00
# include "langcoder.hh"
# include "utils.hh"
2009-04-18 17:20:12 +00:00
# include "wstring_qt.hh"
2009-01-28 20:55:45 +00:00
# include <QFile>
2013-06-02 11:20:33 +00:00
# include <QTextDocumentFragment>
2023-04-18 00:41:47 +00:00
# include <QUrl>
2009-01-28 20:55:45 +00:00
using std : : vector ;
using std : : string ;
2009-04-18 17:20:12 +00:00
using gd : : wstring ;
2009-01-28 20:55:45 +00:00
using std : : set ;
2009-03-26 19:00:08 +00:00
using std : : list ;
2009-01-28 20:55:45 +00:00
2023-03-22 20:34:05 +00:00
inline bool ankiConnectEnabled ( ) { return GlobalBroadcaster : : instance ( ) - > getPreference ( ) - > ankiConnectServer . enabled ; }
2009-01-28 20:55:45 +00:00
ArticleMaker : : ArticleMaker ( vector < sptr < Dictionary : : Class > > const & dictionaries_ ,
2009-05-11 11:03:36 +00:00
vector < Instances : : Group > const & groups_ ,
2023-03-21 07:58:58 +00:00
const Config : : Preferences & cfg_ ) :
2009-01-28 20:55:45 +00:00
dictionaries ( dictionaries_ ) ,
2009-05-11 11:03:36 +00:00
groups ( groups_ ) ,
2023-03-21 07:58:58 +00:00
cfg ( cfg_ )
2009-01-28 20:55:45 +00:00
{
}
2009-05-11 11:03:36 +00:00
2009-02-01 00:08:08 +00:00
std : : string ArticleMaker : : makeHtmlHeader ( QString const & word ,
2014-04-22 13:47:02 +00:00
QString const & icon ,
bool expandOptionalParts ) const
2009-01-28 20:55:45 +00:00
{
2023-04-27 04:08:59 +00:00
string result = R " (<!DOCTYPE html>
< html > < head >
< meta charset = " utf-8 " >
) " ;
2009-01-28 20:55:45 +00:00
2021-12-19 10:37:27 +00:00
// add jquery
{
2023-04-27 04:08:59 +00:00
result + = R " (<script src= " qrc : ///scripts/jquery-3.6.0.slim.min.js"></script>)";
result + = R " (<script> var $_$=$.noConflict(); </script>) " ;
result + = R " (<script src= " qrc : ///scripts/gd-custom.js"></script>)";
result + = R " (<script src= " qrc : ///scripts/iframeResizer.min.js"></script>)";
2021-12-19 10:37:27 +00:00
}
// add qwebchannel
{
2023-04-27 04:08:59 +00:00
result + = R " (<script src= " qrc : ///qtwebchannel/qwebchannel.js"></script>)";
2021-12-19 10:37:27 +00:00
}
2009-05-01 12:20:33 +00:00
2021-12-19 10:37:27 +00:00
// document ready ,init webchannel
{
2023-04-15 07:39:49 +00:00
result + = R " (
< script >
$ _ $ ( document ) . ready ( function ( $ ) {
console . log ( " webchannel ready... " ) ;
new QWebChannel ( qt . webChannelTransport , function ( channel ) {
window . articleview = channel . objects . articleview ;
} ) ;
} ) ;
< / script >
) " ;
2021-12-19 10:37:27 +00:00
}
// Add a css stylesheet
2009-05-01 12:20:33 +00:00
{
2022-12-24 22:01:50 +00:00
result + = R " (<link href= " qrc : ///article-style.css" media="all" rel="stylesheet" type="text/css">)";
2016-08-02 14:56:35 +00:00
2023-03-21 07:58:58 +00:00
if ( cfg . displayStyle . size ( ) )
2009-05-11 11:03:36 +00:00
{
// Load an additional stylesheet
2023-03-21 07:58:58 +00:00
QString displayStyleCssFile = QString ( " qrc:///article-style-st-%1.css " ) . arg ( cfg . displayStyle ) ;
2022-01-12 13:48:03 +00:00
result + = " <link href= \" " + displayStyleCssFile . toStdString ( ) +
2022-12-24 22:01:50 +00:00
R " ( " media = " all " rel = " stylesheet " type = " text/css " > ) " ;
2009-05-11 11:03:36 +00:00
}
2022-01-12 13:48:03 +00:00
result + = readCssFile ( Config : : getUserCssFileName ( ) , " all " ) ;
2012-12-10 14:14:13 +00:00
2023-03-21 07:58:58 +00:00
if ( ! cfg . addonStyle . isEmpty ( ) )
2012-12-10 14:14:13 +00:00
{
2023-03-21 07:58:58 +00:00
QString name = Config : : getStylesDir ( ) + cfg . addonStyle
2012-12-10 14:14:13 +00:00
+ QDir : : separator ( ) + " article-style.css " ;
2022-01-12 13:48:03 +00:00
result + = readCssFile ( name , " all " ) ;
2012-12-10 14:14:13 +00:00
}
2012-09-16 10:19:47 +00:00
// Turn on/off expanding of article optional parts
2014-04-22 13:47:02 +00:00
if ( expandOptionalParts )
2016-08-02 14:56:35 +00:00
{
2023-04-15 07:39:49 +00:00
result + = R " (<!-- Expand optional parts css -->
< style type = " text/css " media = " all " >
. dsl_opt {
display : inline ;
}
. hidden_expand_opt { display : none ; }
< / style > ) " ;
2016-08-02 14:56:35 +00:00
}
2012-09-16 10:19:47 +00:00
2009-05-01 12:20:33 +00:00
}
2009-01-28 20:55:45 +00:00
2009-05-01 12:20:33 +00:00
// Add print-only css
{
2022-12-24 22:01:50 +00:00
result + = R " (<link href= " qrc : ///article-style-print.css" media="print" rel="stylesheet" type="text/css">)";
2009-01-28 20:55:45 +00:00
2022-01-12 13:48:03 +00:00
result + = readCssFile ( Config : : getUserCssPrintFileName ( ) , " print " ) ;
2012-12-10 14:14:13 +00:00
2023-03-21 07:58:58 +00:00
if ( ! cfg . addonStyle . isEmpty ( ) )
2012-12-10 14:14:13 +00:00
{
2023-03-21 07:58:58 +00:00
QString name = Config : : getStylesDir ( ) + cfg . addonStyle
2012-12-10 14:14:13 +00:00
+ QDir : : separator ( ) + " article-style-print.css " ;
2022-01-12 13:48:03 +00:00
result + = readCssFile ( name , " print " ) ;
2012-12-10 14:14:13 +00:00
}
2009-05-01 12:20:33 +00:00
}
2012-12-10 14:14:13 +00:00
2022-03-31 09:51:22 +00:00
result + = " <title> " + Html : : escape ( word . toStdString ( ) ) + " </title> " ;
2009-02-01 00:08:08 +00:00
// This doesn't seem to be much of influence right now, but we'll keep
// it anyway.
if ( icon . size ( ) )
2023-04-27 04:08:59 +00:00
result + = R " (<link rel= " icon " type= " image / png " href= " qrc : ///flags/)" + Html::escape( icon.toUtf8().data() ) + "\" >\n";
2009-02-01 00:08:08 +00:00
2023-04-15 07:39:49 +00:00
result + = QString : : fromUtf8 ( R " (
2023-04-27 04:08:59 +00:00
< script >
2023-04-15 07:39:49 +00:00
function tr ( key ) {
var tr_map = {
" Expand article " : " %1 " , " Collapse article " : " %2 "
} ;
return tr_map [ key ] | | ' ' ;
}
< / script >
) " ).arg( tr( " Expand article " ), tr( " Collapse article " ) )
. toStdString ( ) ;
2023-04-27 04:08:59 +00:00
result + = R " (<script src= " qrc : ///scripts/gd-builtin.js"></script>)";
2022-12-24 00:56:52 +00:00
2022-12-26 17:26:29 +00:00
if ( GlobalBroadcaster : : instance ( ) - > getPreference ( ) - > darkReaderMode )
2022-12-24 00:56:52 +00:00
{
2023-05-09 10:49:44 +00:00
//only enable this darkmode on modern style.
if ( cfg . displayStyle = = " modern " ) {
result + = R " (<link href= " qrc : ///article-style-darkmode.css" media="all" rel="stylesheet" type="text/css">)";
}
2023-04-19 15:20:12 +00:00
2023-03-20 04:44:28 +00:00
// #242525 because Darkreader will invert pure white to this value
2022-12-26 17:26:29 +00:00
result + = R " (
< script src = " qrc:///scripts/darkreader.js " > < / script >
2023-03-20 04:44:28 +00:00
< style >
body { background : # 242525 ; }
. gdarticle { background : initial ; }
2023-03-28 08:59:24 +00:00
2023-04-19 15:20:12 +00:00
. gdarticlebody img {
2023-03-28 08:59:24 +00:00
background : white ;
}
2023-03-20 04:44:28 +00:00
< / style >
2022-12-26 17:26:29 +00:00
< script >
2023-03-01 21:19:54 +00:00
// This function returns a promise, but it is synchroneous because it does not use await
function fetchShim ( src ) {
if ( src . startsWith ( ' gdlookup : //')) {
// See https://github.com/xiaoyifang/goldendict/issues/363
console . error ( ' Dark Reader discovered unexpected URL ' , src ) ;
return Promise . resolve ( { blob : ( ) = > new Blob ( ) } ) ;
}
2023-04-19 15:20:12 +00:00
if ( src . startsWith ( ' qrcx : //') || src.startsWith('qrc://') || src.startsWith('bres://') || src.startsWith('gico://')) {
2023-03-01 21:19:54 +00:00
// This is a resource URL, need to fetch and transform
return new Promise ( ( resolve ) = > {
const img = document . createElement ( ' img ' ) ;
img . addEventListener ( ' load ' , ( ) = > {
// Set willReadFrequently to true to tell engine to store data in RAM-backed buffer and not on GPU
const canvas = document . createElement ( ' canvas ' , { willReadFrequently : true } ) ;
canvas . width = img . naturalWidth ;
canvas . height = img . naturalHeight ;
const ctx = canvas . getContext ( ' 2 d ' ) ;
ctx . drawImage ( img , 0 , 0 ) ;
canvas . toBlob ( ( blob ) = > {
resolve ( { blob : ( ) = > blob } ) ;
} ) ;
} , false ) ;
img . src = src ;
} ) ;
}
// This is a standard URL, can fetch it directly
return fetch ( src ) ;
}
DarkReader . setFetchMethod ( fetchShim ) ;
2022-12-26 17:26:29 +00:00
DarkReader . enable ( {
brightness : 100 ,
contrast : 90 ,
sepia : 10
} ) ;
< / script >
) " ;
2022-12-24 00:56:52 +00:00
}
2009-02-01 00:08:08 +00:00
result + = " </head><body> " ;
return result ;
}
2022-01-12 13:48:03 +00:00
std : : string ArticleMaker : : readCssFile ( QString const & fileName , std : : string media ) const {
QFile addonCss ( fileName ) ;
std : : string result ;
if ( addonCss . open ( QFile : : ReadOnly ) ) {
QByteArray css = addonCss . readAll ( ) ;
if ( ! css . isEmpty ( ) ) {
result + = " <!-- Addon style css --> \n " ;
2022-12-24 22:01:50 +00:00
result + = R " (<style type= " text / css " media= " ) " + media + " \ " > \n " ;
2022-01-12 13:48:03 +00:00
result + = css . data ( ) ;
result + = " </style> \n " ;
}
}
return result ;
}
2009-05-11 11:03:36 +00:00
std : : string ArticleMaker : : makeNotFoundBody ( QString const & word ,
QString const & group )
2009-02-01 00:08:08 +00:00
{
2010-03-30 13:41:14 +00:00
string result ( " <div class= \" gdnotfound \" ><p> " ) ;
2009-02-01 00:08:08 +00:00
2013-07-16 13:59:56 +00:00
QString str ( word ) ;
if ( str . isRightToLeft ( ) )
{
str . insert ( 0 , ( ushort ) 0x202E ) ; // RLE, Right-to-Left Embedding
str . append ( ( ushort ) 0x202C ) ; // PDF, POP DIRECTIONAL FORMATTING
}
2010-03-30 13:41:14 +00:00
if ( word . size ( ) )
result + = tr ( " No translation for <b>%1</b> was found in group <b>%2</b>. " ) .
2023-04-08 03:50:45 +00:00
arg ( QString : : fromUtf8 ( Html : : escape ( str . toUtf8 ( ) . data ( ) ) . c_str ( ) ) , QString : : fromUtf8 ( Html : : escape ( group . toUtf8 ( ) . data ( ) ) . c_str ( ) ) ) .
2010-03-30 13:41:14 +00:00
toUtf8 ( ) . data ( ) ;
else
result + = tr ( " No translation was found in group <b>%1</b>. " ) .
arg ( QString : : fromUtf8 ( Html : : escape ( group . toUtf8 ( ) . data ( ) ) . c_str ( ) ) ) .
toUtf8 ( ) . data ( ) ;
result + = " </p></div> " ;
return result ;
2009-03-26 19:00:08 +00:00
}
2021-06-10 16:13:11 +00:00
sptr < Dictionary : : DataRequest > ArticleMaker : : makeDefinitionFor (
Config : : InputPhrase const & phrase , unsigned groupId ,
2009-09-23 18:44:38 +00:00
QMap < QString , QString > const & contexts ,
2014-04-16 16:18:28 +00:00
QSet < QString > const & mutedDicts ,
2018-06-13 16:00:42 +00:00
QStringList const & dictIDs , bool ignoreDiacritics ) const
2009-03-26 19:00:08 +00:00
{
2014-04-16 16:18:28 +00:00
if ( ! dictIDs . isEmpty ( ) )
{
QStringList ids = dictIDs ;
std : : vector < sptr < Dictionary : : Class > > ftsDicts ;
// Find dictionaries by ID's
for ( unsigned x = 0 ; x < dictionaries . size ( ) ; x + + )
{
for ( QStringList : : Iterator it = ids . begin ( ) ; it ! = ids . end ( ) ; + + it )
{
if ( * it = = QString : : fromStdString ( dictionaries [ x ] - > getId ( ) ) )
{
ftsDicts . push_back ( dictionaries [ x ] ) ;
ids . erase ( it ) ;
break ;
}
}
2014-04-22 13:47:02 +00:00
if ( ids . isEmpty ( ) )
break ;
2014-04-16 16:18:28 +00:00
}
2021-06-10 16:13:11 +00:00
string header = makeHtmlHeader ( phrase . phrase , QString ( ) , true ) ;
2014-04-16 16:18:28 +00:00
2023-05-14 05:49:20 +00:00
return std : : make_shared < ArticleRequest > ( phrase , Instances : : Group { groupId , " " } ,
2014-04-16 16:18:28 +00:00
contexts , ftsDicts , header ,
2014-04-22 13:47:02 +00:00
- 1 , true ) ;
2014-04-16 16:18:28 +00:00
}
2010-05-08 14:01:59 +00:00
if ( groupId = = Instances : : Group : : HelpGroupId )
2009-02-08 18:35:29 +00:00
{
// This is a special group containing internal welcome/help pages
2023-03-21 06:00:23 +00:00
string result = makeHtmlHeader ( phrase . phrase , QString ( ) , cfg . alwaysExpandOptionalParts ) ;
2013-05-31 03:28:05 +00:00
2021-06-10 16:13:11 +00:00
if ( phrase . phrase = = tr ( " Welcome! " ) )
2009-02-08 18:35:29 +00:00
{
result + = tr (
" <h3 align= \" center \" >Welcome to <b>GoldenDict</b>!</h3> "
2009-05-24 17:38:38 +00:00
" <p>To start working with the program, first visit <b>Edit|Dictionaries</b> to add some directory paths where to search "
" for the dictionary files, set up various Wikipedia sites or other sources, adjust dictionary order or create dictionary groups. "
2009-02-08 21:32:33 +00:00
" <p>And then you're ready to look up your words! You can do that in this window "
2009-02-08 18:35:29 +00:00
" by using a pane to the left, or you can <a href= \" Working with popup \" >look up words from other active applications</a>. "
2009-05-24 17:38:38 +00:00
" <p>To customize program, check out the available preferences at <b>Edit|Preferences</b>. "
" All settings there have tooltips, be sure to read them if you are in doubt about anything. "
2009-02-08 18:35:29 +00:00
" <p>Should you need further help, have any questions, "
2023-01-06 01:34:53 +00:00
" suggestions or just wonder what the others think, you are welcome at the program's <a href= \" https://github.com/xiaoyifang/goldendict/discussions \" >forum</a>. "
" <p>Check program's <a href= \" https://github.com/xiaoyifang/goldendict \" >website</a> for the updates. "
2013-01-07 09:30:31 +00:00
" <p>(c) 2008-2013 Konstantin Isakov. Licensed under GPLv3 or later. "
2013-05-31 03:28:05 +00:00
2009-02-08 18:35:29 +00:00
) . toUtf8 ( ) . data ( ) ;
}
else
2021-06-10 16:13:11 +00:00
if ( phrase . phrase = = tr ( " Working with popup " ) )
2009-02-08 18:35:29 +00:00
{
result + = ( tr ( " <h3 align= \" center \" >Working with the popup</h3> "
" To look up words from other active applications, you would need to first activate the <i> \" Scan popup functionality \" </i> in <b>Preferences</b>, "
" and then enable it at any time either by triggering the 'Popup' icon above, or "
" by clicking the tray icon down below with your right mouse button and choosing so in the menu you've popped. " ) +
# ifdef Q_OS_WIN32
tr ( " Then just stop the cursor over the word you want to look up in another application, "
" and a window would pop up which would describe it to you. " )
# else
tr ( " Then just select any word you want to look up in another application by your mouse "
" (double-click it or swipe it with mouse with the button pressed), "
" and a window would pop up which would describe the word to you. " )
# endif
) . toUtf8 ( ) . data ( ) ;
}
else
{
// Not found
2021-06-10 16:13:11 +00:00
return makeNotFoundTextFor ( phrase . phrase , " help " ) ;
2009-02-08 18:35:29 +00:00
}
2013-05-31 03:28:05 +00:00
2009-02-08 18:35:29 +00:00
result + = " </body></html> " ;
2022-11-29 03:54:31 +00:00
sptr < Dictionary : : DataRequestInstant > r = std : : make_shared < Dictionary : : DataRequestInstant > ( true ) ;
2009-03-26 19:00:08 +00:00
2023-03-26 06:46:27 +00:00
r - > appendDataSlice ( result . data ( ) , result . size ( ) ) ;
2009-03-26 19:00:08 +00:00
return r ;
2009-02-08 18:35:29 +00:00
}
2009-03-26 19:00:08 +00:00
2009-01-28 20:55:45 +00:00
// Find the given group
Instances : : Group const * activeGroup = 0 ;
for ( unsigned x = 0 ; x < groups . size ( ) ; + + x )
2009-04-10 12:48:40 +00:00
if ( groups [ x ] . id = = groupId )
2009-01-28 20:55:45 +00:00
{
activeGroup = & groups [ x ] ;
break ;
}
// If we've found a group, use its dictionaries; otherwise, use the global
// heap.
std : : vector < sptr < Dictionary : : Class > > const & activeDicts =
activeGroup ? activeGroup - > dictionaries : dictionaries ;
2021-06-10 16:13:11 +00:00
string header = makeHtmlHeader ( phrase . phrase ,
2009-02-01 00:08:08 +00:00
activeGroup & & activeGroup - > icon . size ( ) ?
2014-04-22 13:47:02 +00:00
activeGroup - > icon : QString ( ) ,
2023-03-21 06:00:23 +00:00
cfg . alwaysExpandOptionalParts ) ;
2009-01-28 20:55:45 +00:00
2009-09-23 18:44:38 +00:00
if ( mutedDicts . size ( ) )
{
std : : vector < sptr < Dictionary : : Class > > unmutedDicts ;
unmutedDicts . reserve ( activeDicts . size ( ) ) ;
for ( unsigned x = 0 ; x < activeDicts . size ( ) ; + + x )
if ( ! mutedDicts . contains (
QString : : fromStdString ( activeDicts [ x ] - > getId ( ) ) ) )
unmutedDicts . push_back ( activeDicts [ x ] ) ;
2023-05-12 02:57:57 +00:00
return std : : make_shared < ArticleRequest > ( phrase , Instances : : Group { activeGroup ? activeGroup - > id : 0 , activeGroup ? activeGroup - > name : " " } ,
2013-06-02 11:20:33 +00:00
contexts , unmutedDicts , header ,
2023-03-21 07:58:58 +00:00
cfg . collapseBigArticles ? cfg . articleSizeLimit : - 1 ,
2023-03-21 06:00:23 +00:00
cfg . alwaysExpandOptionalParts , ignoreDiacritics ) ;
2009-09-23 18:44:38 +00:00
}
else
2023-05-12 02:57:57 +00:00
return std : : make_shared < ArticleRequest > ( phrase , Instances : : Group { activeGroup ? activeGroup - > id : 0 , activeGroup ? activeGroup - > name : " " } ,
2013-06-02 11:20:33 +00:00
contexts , activeDicts , header ,
2023-03-21 07:58:58 +00:00
cfg . collapseBigArticles ? cfg . articleSizeLimit : - 1 ,
2023-03-21 06:00:23 +00:00
cfg . alwaysExpandOptionalParts , ignoreDiacritics ) ;
2009-03-26 19:00:08 +00:00
}
sptr < Dictionary : : DataRequest > ArticleMaker : : makeNotFoundTextFor (
QString const & word , QString const & group ) const
{
2014-04-22 13:47:02 +00:00
string result = makeHtmlHeader ( word , QString ( ) , true ) + makeNotFoundBody ( word , group ) +
2009-03-26 19:00:08 +00:00
" </body></html> " ;
2022-11-29 03:54:31 +00:00
sptr < Dictionary : : DataRequestInstant > r = std : : make_shared < Dictionary : : DataRequestInstant > ( true ) ;
2009-03-26 19:00:08 +00:00
2023-03-26 06:46:27 +00:00
r - > appendDataSlice ( result . data ( ) , result . size ( ) ) ;
2009-03-26 19:00:08 +00:00
return r ;
}
2009-05-11 15:33:57 +00:00
sptr < Dictionary : : DataRequest > ArticleMaker : : makeEmptyPage ( ) const
{
2014-04-22 13:47:02 +00:00
string result = makeHtmlHeader ( tr ( " (untitled) " ) , QString ( ) , true ) +
2009-05-11 15:33:57 +00:00
" </body></html> " ;
sptr < Dictionary : : DataRequestInstant > r =
2022-11-29 03:54:31 +00:00
std : : make_shared < Dictionary : : DataRequestInstant > ( true ) ;
2009-05-11 15:33:57 +00:00
2023-03-26 06:46:27 +00:00
r - > appendDataSlice ( result . data ( ) , result . size ( ) ) ;
2009-05-11 15:33:57 +00:00
return r ;
}
2012-12-07 11:59:29 +00:00
sptr < Dictionary : : DataRequest > ArticleMaker : : makePicturePage ( string const & url ) const
{
2014-04-22 13:47:02 +00:00
string result = makeHtmlHeader ( tr ( " (picture) " ) , QString ( ) , true )
2012-12-08 09:09:21 +00:00
+ " <a href= \" javascript: if(history.length>2) history.go(-1) \" > "
+ " <img src= \" " + url + " \" /></a> "
2012-12-07 11:59:29 +00:00
+ " </body></html> " ;
sptr < Dictionary : : DataRequestInstant > r =
2022-11-29 03:54:31 +00:00
std : : make_shared < Dictionary : : DataRequestInstant > ( true ) ;
2012-12-07 11:59:29 +00:00
2023-03-26 06:46:27 +00:00
r - > appendDataSlice ( result . data ( ) , result . size ( ) ) ;
2012-12-07 11:59:29 +00:00
return r ;
}
2013-06-02 11:20:33 +00:00
2012-12-13 20:21:33 +00:00
bool ArticleMaker : : adjustFilePath ( QString & fileName )
{
QFileInfo info ( fileName ) ;
if ( ! info . isFile ( ) )
{
QString dir = Config : : getConfigDir ( ) ;
dir . chop ( 1 ) ;
info . setFile ( dir + fileName ) ;
if ( info . isFile ( ) )
{
fileName = info . canonicalFilePath ( ) ;
return true ;
}
}
return false ;
}
2009-03-26 19:00:08 +00:00
//////// ArticleRequest
2023-03-22 20:34:05 +00:00
ArticleRequest : : ArticleRequest ( Config : : InputPhrase const & phrase ,
2023-05-12 02:57:57 +00:00
Instances : : Group const & group_ ,
2023-03-22 20:34:05 +00:00
QMap < QString , QString > const & contexts_ ,
vector < sptr < Dictionary : : Class > > const & activeDicts_ ,
string const & header ,
int sizeLimit ,
bool needExpandOptionalParts_ ,
bool ignoreDiacritics_ ) :
word ( phrase . phrase ) ,
group ( group_ ) ,
contexts ( contexts_ ) ,
activeDicts ( activeDicts_ ) ,
articleSizeLimit ( sizeLimit ) ,
needExpandOptionalParts ( needExpandOptionalParts_ ) ,
ignoreDiacritics ( ignoreDiacritics_ )
2009-03-26 19:00:08 +00:00
{
2021-06-10 16:13:11 +00:00
if ( ! phrase . punctuationSuffix . isEmpty ( ) )
alts . insert ( gd : : toWString ( phrase . phraseWithSuffix ( ) ) ) ;
2009-03-26 19:00:08 +00:00
// No need to lock dataMutex on construction
hasAnyData = true ;
2023-03-26 06:46:27 +00:00
appendDataSlice ( ( void * ) header . data ( ) , header . size ( ) ) ;
2009-01-29 19:16:25 +00:00
2022-11-04 13:23:42 +00:00
//clear founded dicts.
2023-05-12 02:57:57 +00:00
emit GlobalBroadcaster : : instance ( ) - > dictionaryClear ( ActiveDictIds { group . id , word } ) ;
2009-01-28 20:55:45 +00:00
2022-11-04 13:23:42 +00:00
// Accumulate main forms
2009-03-26 19:00:08 +00:00
for ( unsigned x = 0 ; x < activeDicts . size ( ) ; + + x )
{
2023-04-17 12:55:39 +00:00
sptr < Dictionary : : WordSearchRequest > s = activeDicts [ x ] - > findHeadwordsForSynonym ( gd : : removeTrailingZero ( word ) ) ;
2009-03-26 19:00:08 +00:00
2022-12-26 02:08:17 +00:00
connect ( s . get ( ) , & Dictionary : : Request : : finished , this , & ArticleRequest : : altSearchFinished , Qt : : QueuedConnection ) ;
2009-03-26 19:00:08 +00:00
altSearches . push_back ( s ) ;
}
altSearchFinished ( ) ; // Handle any ones which have already finished
}
2009-01-28 20:55:45 +00:00
2009-03-26 19:00:08 +00:00
void ArticleRequest : : altSearchFinished ( )
{
if ( altsDone )
return ;
2013-05-31 03:28:05 +00:00
2009-03-26 19:00:08 +00:00
// Check every request for finishing
for ( list < sptr < Dictionary : : WordSearchRequest > > : : iterator i =
altSearches . begin ( ) ; i ! = altSearches . end ( ) ; )
{
if ( ( * i ) - > isFinished ( ) )
{
// This one's finished
for ( size_t count = ( * i ) - > matchesCount ( ) , x = 0 ; x < count ; + + x )
alts . insert ( ( * * i ) [ x ] . word ) ;
altSearches . erase ( i + + ) ;
}
else
+ + i ;
}
if ( altSearches . empty ( ) )
2009-01-28 20:55:45 +00:00
{
2013-09-20 14:25:44 +00:00
# ifdef QT_DEBUG
2022-05-29 02:05:44 +00:00
qDebug ( " alts finished " ) ;
2013-09-20 14:25:44 +00:00
# endif
2013-05-31 03:28:05 +00:00
2009-03-26 19:00:08 +00:00
// They all've finished! Now we can look up bodies
altsDone = true ; // So any pending signals in queued mode won't mess us up
vector < wstring > altsVector ( alts . begin ( ) , alts . end ( ) ) ;
2013-05-31 03:28:05 +00:00
2013-09-20 14:25:44 +00:00
# ifdef QT_DEBUG
2009-03-26 19:00:08 +00:00
for ( unsigned x = 0 ; x < altsVector . size ( ) ; + + x )
{
2023-04-16 09:07:07 +00:00
qDebug ( ) < < " Alt: " < < QString : : fromStdU32String ( altsVector [ x ] ) ;
2009-03-26 19:00:08 +00:00
}
2013-09-20 14:25:44 +00:00
# endif
2009-03-26 19:00:08 +00:00
2009-04-18 17:20:12 +00:00
wstring wordStd = gd : : toWString ( word ) ;
2009-01-28 20:55:45 +00:00
2013-06-03 17:14:05 +00:00
if ( activeDicts . size ( ) < = 1 )
articleSizeLimit = - 1 ; // Don't collapse article if only one dictionary presented
2009-01-28 20:55:45 +00:00
for ( unsigned x = 0 ; x < activeDicts . size ( ) ; + + x )
{
2013-09-19 19:43:16 +00:00
try
{
sptr < Dictionary : : DataRequest > r =
activeDicts [ x ] - > getArticle ( wordStd , altsVector ,
2023-04-17 12:55:39 +00:00
gd : : removeTrailingZero ( contexts . value ( QString : : fromStdString ( activeDicts [ x ] - > getId ( ) ) ) ) ,
2018-06-13 16:00:42 +00:00
ignoreDiacritics ) ;
2009-01-28 20:55:45 +00:00
2022-12-26 02:08:17 +00:00
connect ( r . get ( ) , & Dictionary : : Request : : finished , this , & ArticleRequest : : bodyFinished , Qt : : QueuedConnection ) ;
2009-03-26 19:00:08 +00:00
2013-09-19 19:43:16 +00:00
bodyRequests . push_back ( r ) ;
}
catch ( std : : exception & e )
{
2013-11-16 18:34:09 +00:00
gdWarning ( " getArticle request error (%s) in \" %s \" \n " ,
e . what ( ) , activeDicts [ x ] - > getName ( ) . c_str ( ) ) ;
2013-09-19 19:43:16 +00:00
}
2009-01-28 20:55:45 +00:00
}
2009-03-26 19:00:08 +00:00
bodyFinished ( ) ; // Handle any ones which have already finished
2009-01-28 20:55:45 +00:00
}
2009-03-26 19:00:08 +00:00
}
2009-01-28 20:55:45 +00:00
2013-06-03 13:38:57 +00:00
int ArticleRequest : : findEndOfCloseDiv ( const QString & str , int pos )
{
for ( ; ; )
{
2023-04-16 01:06:29 +00:00
const int n1 = str . indexOf ( " </div> " , pos ) ;
2013-06-03 13:38:57 +00:00
if ( n1 < = 0 )
return n1 ;
2023-04-16 01:06:29 +00:00
// will there be some custom tags starts with <div but not <div> ,such as <divider>
const int n2 = str . indexOf ( RX : : Html : : startDivTag , pos ) ;
2013-06-03 13:38:57 +00:00
if ( n2 < = 0 | | n2 > n1 )
return n1 + 6 ;
pos = findEndOfCloseDiv ( str , n2 + 1 ) ;
if ( pos < = 0 )
return pos ;
}
}
2023-04-15 02:48:12 +00:00
bool ArticleRequest : : isCollapsable ( Dictionary : : DataRequest & req , QString const & dictId ) {
if ( GlobalBroadcaster : : instance ( ) - > collapsedDicts . contains ( dictId ) )
return true ;
bool collapse = false ;
if ( articleSizeLimit > = 0 )
{
try
{
Mutex : : Lock _ ( dataMutex ) ;
QString text = QString : : fromUtf8 ( req . getFullData ( ) . data ( ) , req . getFullData ( ) . size ( ) ) ;
if ( ! needExpandOptionalParts )
{
// Strip DSL optional parts
for ( ; ; )
{
2023-04-15 11:10:15 +00:00
const int pos = text . indexOf ( " <div class= \" dsl_opt \" " ) ;
2023-04-15 02:48:12 +00:00
if ( pos > 0 )
{
2023-04-15 11:10:15 +00:00
const int endPos = findEndOfCloseDiv ( text , pos + 1 ) ;
2023-04-15 02:48:12 +00:00
if ( endPos > pos )
text . remove ( pos , endPos - pos ) ;
else
break ;
}
else
break ;
}
}
int size = htmlTextSize ( text ) ;
if ( size > articleSizeLimit )
collapse = true ;
}
catch ( . . . )
{
}
}
return collapse ;
}
2009-03-26 19:00:08 +00:00
void ArticleRequest : : bodyFinished ( )
{
if ( bodyDone )
return ;
2009-01-28 20:55:45 +00:00
2022-05-29 02:05:44 +00:00
GD_DPRINTF ( " some body finished " ) ;
2013-05-31 03:28:05 +00:00
2009-03-26 19:00:08 +00:00
bool wasUpdated = false ;
2013-05-31 03:28:05 +00:00
2022-01-08 06:51:24 +00:00
QStringList dictIds ;
2009-03-26 19:00:08 +00:00
while ( bodyRequests . size ( ) )
2009-01-28 20:55:45 +00:00
{
2009-03-26 19:00:08 +00:00
// Since requests should go in order, check the first one first
if ( bodyRequests . front ( ) - > isFinished ( ) )
2009-01-28 20:55:45 +00:00
{
2009-03-26 19:00:08 +00:00
// Good
2022-05-29 02:05:44 +00:00
GD_DPRINTF ( " one finished. " ) ;
2009-03-26 19:00:08 +00:00
Dictionary : : DataRequest & req = * bodyRequests . front ( ) ;
QString errorString = req . getErrorString ( ) ;
if ( req . dataSize ( ) > = 0 | | errorString . size ( ) )
{
2010-04-03 09:43:39 +00:00
sptr < Dictionary : : Class > const & activeDict =
activeDicts [ activeDicts . size ( ) - bodyRequests . size ( ) ] ;
string dictId = activeDict - > getId ( ) ;
2022-01-08 06:51:24 +00:00
dictIds < < QString : : fromStdString ( dictId ) ;
2009-04-12 16:22:42 +00:00
string head ;
2009-05-11 19:14:28 +00:00
string gdFrom = " gdfrom- " + Html : : escape ( dictId ) ;
2009-04-12 16:22:42 +00:00
if ( closePrevSpan )
{
2022-12-24 22:01:50 +00:00
head + = R " (</div></div><div style= " clear : both ; " ></div><span class= " gdarticleseparator " ></span>) " ;
2009-04-12 16:22:42 +00:00
}
2023-04-15 02:48:12 +00:00
bool collapse = isCollapsable ( req , QString : : fromStdString ( dictId ) ) ;
2013-06-02 11:20:33 +00:00
2009-04-12 16:22:42 +00:00
string jsVal = Html : : escapeForJavaScript ( dictId ) ;
2009-05-11 22:25:22 +00:00
2023-04-15 07:39:49 +00:00
head + = QString : : fromUtf8 (
2023-05-12 01:20:19 +00:00
R " ( <div class= " gdarticle % 1 % 2 " id= " % 3 "
onClick = " gdMakeArticleActive( '%4', false ); "
onContextMenu = " gdMakeArticleActive( '%4', false ); " > ) " )
2023-04-15 07:39:49 +00:00
. arg ( closePrevSpan ? " " : " gdactivearticle " ,
collapse ? " gdcollapsedarticle " : " " ,
gdFrom . c_str ( ) ,
jsVal . c_str ( ) )
. toStdString ( ) ;
2009-04-12 16:22:42 +00:00
closePrevSpan = true ;
2010-04-03 09:43:39 +00:00
2023-04-15 07:39:49 +00:00
head + = QString : : fromUtf8 (
R " (<div class= " gddictname " onclick= " gdExpandArticle ( ' % 1 ' ) ; " %2 id= " gddictname - % 1 " title= " % 3 " >
< span class = " gddicticon " ><img src= " gico : //%1/dicticon.png"></span>
< span class = " gdfromprefix " >%4</span>
< span class = " gddicttitle " >%5</span>
2023-04-20 09:49:05 +00:00
< span class = " collapse_expand_area " ><img class= " % 6 " id= " expandicon - % 1 " title= " % 7 " ></span>
2023-04-15 07:39:49 +00:00
< / div > ) " )
. arg ( dictId . c_str ( ) ,
collapse ? R " (style= " cursor : pointer ; " ) " : " " ,
collapse ? tr ( " Expand article " ) : QString ( ) ,
Html : : escape ( tr ( " From " ) . toStdString ( ) ) . c_str ( ) ,
Html : : escape ( activeDict - > getName ( ) ) . c_str ( ) ,
collapse ? " gdexpandicon " : " gdcollapseicon " ,
collapse ? " " : tr ( " Collapse article " )
)
. toStdString ( ) ;
head + = R " (<div class= " gddictnamebodyseparator " ></div>) " ;
2009-03-26 19:00:08 +00:00
2023-03-22 20:34:05 +00:00
// If the user has enabled Anki integration in settings,
// Show a (+) button that lets the user add a new Anki card.
if ( ankiConnectEnabled ( ) ) {
QString link { R " EOF(
< a href = " ankicard:%1 " class = " ankibutton " title= " % 2 " >
< img src = " qrc:///icons/add-anki-icon.svg " >
< / a >
) EOF " };
head + = link . arg ( Html : : escape ( dictId ) . c_str ( ) , tr ( " Make a new Anki note " ) ) . toStdString ( ) ;
}
2023-04-15 07:39:49 +00:00
head + = QString : : fromUtf8 (
R " (<div class= " gdarticlebody gdlangfrom - % 1 " lang= " % 2 " style= " display : % 3 " id= " gdarticlefrom - % 4 " >) " )
. arg ( LangCoder : : intToCode2 ( activeDict - > getLangFrom ( ) ) ,
LangCoder : : intToCode2 ( activeDict - > getLangTo ( ) ) ,
collapse ? " none " : " inline " ,
dictId . c_str ( ) )
. toStdString ( ) ;
2010-04-03 09:43:39 +00:00
2023-03-26 06:46:27 +00:00
if ( errorString . size ( ) ) {
head + = " <div class= \" gderrordesc \" > "
+ Html : : escape ( tr ( " Query error: %1 " ) . arg ( errorString ) . toUtf8 ( ) . data ( ) ) + " </div> " ;
2009-03-26 19:00:08 +00:00
}
2023-03-26 06:46:27 +00:00
appendDataSlice ( head . data ( ) , head . size ( ) ) ;
2013-05-31 03:28:05 +00:00
2023-03-26 06:46:27 +00:00
try {
if ( req . dataSize ( ) > 0 ) {
auto d = bodyRequests . front ( ) - > getFullData ( ) ;
appendDataSlice ( & d . front ( ) , d . size ( ) ) ;
}
2013-09-19 19:43:16 +00:00
}
2023-03-26 06:46:27 +00:00
catch ( std : : exception & e ) {
2013-11-16 18:34:09 +00:00
gdWarning ( " getDataSlice error: %s \n " , e . what ( ) ) ;
2013-09-19 19:43:16 +00:00
}
2009-01-28 20:55:45 +00:00
2009-03-26 19:00:08 +00:00
wasUpdated = true ;
2009-01-28 20:55:45 +00:00
2009-03-26 19:00:08 +00:00
foundAnyDefinitions = true ;
}
2022-05-29 02:05:44 +00:00
GD_DPRINTF ( " erasing.. " ) ;
2009-03-26 19:00:08 +00:00
bodyRequests . pop_front ( ) ;
2022-05-29 02:05:44 +00:00
GD_DPRINTF ( " erase done.. " ) ;
2009-01-28 20:55:45 +00:00
}
2009-03-26 19:00:08 +00:00
else
2009-01-28 20:55:45 +00:00
{
2022-05-29 02:05:44 +00:00
GD_DPRINTF ( " one not finished. " ) ;
2014-05-10 21:02:31 +00:00
break ;
2009-01-28 20:55:45 +00:00
}
}
2023-05-14 05:49:20 +00:00
ActiveDictIds hittedWord { group . id , word , dictIds } ;
2022-01-08 06:51:24 +00:00
2009-03-26 19:00:08 +00:00
if ( bodyRequests . empty ( ) )
{
// No requests left, end the article
2009-01-28 20:55:45 +00:00
2009-03-26 19:00:08 +00:00
bodyDone = true ;
2013-05-31 03:28:05 +00:00
2009-03-26 19:00:08 +00:00
{
string footer ;
2009-01-28 20:55:45 +00:00
2009-04-12 16:22:42 +00:00
if ( closePrevSpan )
{
2017-06-09 12:38:23 +00:00
footer + = " </div></div> " ;
2009-04-12 16:22:42 +00:00
closePrevSpan = false ;
}
2013-05-31 03:28:05 +00:00
2009-03-26 19:00:08 +00:00
if ( ! foundAnyDefinitions )
{
// No definitions were ever found, say so to the user.
2010-03-30 13:41:14 +00:00
2018-07-07 09:33:15 +00:00
// Larger words are usually whole sentences - don't clutter the output
2010-03-30 13:41:14 +00:00
// with their full bodies.
2023-05-12 02:57:57 +00:00
footer + = ArticleMaker : : makeNotFoundBody ( word . size ( ) < 40 ? word : " " , group . name ) ;
2009-03-26 19:00:08 +00:00
2009-04-17 13:51:50 +00:00
// When there were no definitions, we run stemmed search.
2023-03-26 06:46:27 +00:00
stemmedWordFinder = std : : make_shared < WordFinder > ( this ) ;
2009-04-17 13:51:50 +00:00
2022-12-26 02:08:17 +00:00
connect ( stemmedWordFinder . get ( ) ,
2023-03-26 06:46:27 +00:00
& WordFinder : : finished ,
this ,
& ArticleRequest : : stemmedSearchFinished ,
Qt : : QueuedConnection ) ;
2009-04-17 13:51:50 +00:00
stemmedWordFinder - > stemmedMatch ( word , activeDicts ) ;
}
2023-03-26 06:46:27 +00:00
else {
2023-04-19 23:51:35 +00:00
footer + = R " (<div class= " empty - space " ></div>) " ;
2009-04-17 13:51:50 +00:00
footer + = " </body></html> " ;
}
2009-03-26 19:00:08 +00:00
2023-03-26 06:46:27 +00:00
appendDataSlice ( footer . data ( ) , footer . size ( ) ) ;
2009-03-26 19:00:08 +00:00
}
2023-05-14 05:49:20 +00:00
2023-05-12 02:57:57 +00:00
if ( stemmedWordFinder . get ( ) ) {
2023-03-26 06:46:27 +00:00
update ( ) ;
2023-05-14 05:49:20 +00:00
qDebug ( ) < < " send dicts(stemmed): " < < hittedWord ;
emit GlobalBroadcaster : : instance ( ) - > dictionaryChanges ( hittedWord ) ;
2023-03-26 06:46:27 +00:00
dictIds . clear ( ) ;
2022-01-19 12:16:45 +00:00
}
2022-01-08 06:51:24 +00:00
else {
2009-04-17 13:51:50 +00:00
finish ( ) ;
2023-05-14 05:49:20 +00:00
qDebug ( ) < < " send dicts(finished): " < < hittedWord ;
emit GlobalBroadcaster : : instance ( ) - > dictionaryChanges ( hittedWord ) ;
2022-01-19 12:16:45 +00:00
dictIds . clear ( ) ;
2022-01-08 06:51:24 +00:00
}
2023-03-26 06:46:27 +00:00
}
2023-05-12 02:57:57 +00:00
else if ( wasUpdated ) {
2009-03-26 19:00:08 +00:00
update ( ) ;
2023-05-14 05:49:20 +00:00
qDebug ( ) < < " send dicts(updated): " < < hittedWord ;
emit GlobalBroadcaster : : instance ( ) - > dictionaryChanges ( hittedWord ) ;
2022-01-19 12:16:45 +00:00
dictIds . clear ( ) ;
2022-01-08 13:45:10 +00:00
}
2009-02-01 00:08:08 +00:00
}
2009-03-26 19:00:08 +00:00
2022-04-20 12:53:57 +00:00
int ArticleRequest : : htmlTextSize ( QString html )
{
// website dictionary.
if ( html . contains ( QRegularExpression ( " <iframe \\ s*[^>]*> " , QRegularExpression : : CaseInsensitiveOption ) ) )
{
//arbitary number;
return 1000 ;
}
//https://bugreports.qt.io/browse/QTBUG-102757
QString stripStyleSheet =
html . remove ( QRegularExpression ( " <link \\ s*[^>]*> " , QRegularExpression : : CaseInsensitiveOption ) )
2022-12-24 22:01:50 +00:00
. remove ( QRegularExpression ( R " (<script[ \ s \ S]*?>[ \ s \ S]*?< \ /script>) " , QRegularExpression : : CaseInsensitiveOption | QRegularExpression : : MultilineOption ) ) ;
2022-04-20 12:53:57 +00:00
int size = QTextDocumentFragment : : fromHtml ( stripStyleSheet ) . toPlainText ( ) . length ( ) ;
return size ;
}
2009-04-17 13:51:50 +00:00
void ArticleRequest : : stemmedSearchFinished ( )
{
// Got stemmed matching results
WordFinder : : SearchResults sr = stemmedWordFinder - > getResults ( ) ;
string footer ;
2010-03-30 13:41:14 +00:00
bool continueMatching = false ;
2009-04-17 13:51:50 +00:00
if ( sr . size ( ) )
{
2022-12-24 22:01:50 +00:00
footer + = R " (<div class= " gdstemmedsuggestion " ><span class= " gdstemmedsuggestion_head " >) " +
2009-04-17 13:51:50 +00:00
Html : : escape ( tr ( " Close words: " ) . toUtf8 ( ) . data ( ) ) +
" </span><span class= \" gdstemmedsuggestion_body \" > " ;
for ( unsigned x = 0 ; x < sr . size ( ) ; + + x )
{
2010-03-30 13:41:14 +00:00
footer + = linkWord ( sr [ x ] . first ) ;
2009-04-17 13:51:50 +00:00
if ( x ! = sr . size ( ) - 1 )
{
footer + = " , " ;
}
}
footer + = " </span></div> " ;
}
2010-03-30 13:41:14 +00:00
splittedWords = splitIntoWords ( word ) ;
if ( splittedWords . first . size ( ) > 1 ) // Contains more than one word
{
2022-12-26 02:08:17 +00:00
disconnect ( stemmedWordFinder . get ( ) , & WordFinder : : finished , this , & ArticleRequest : : stemmedSearchFinished ) ;
2010-03-30 13:41:14 +00:00
2022-12-26 02:08:17 +00:00
connect ( stemmedWordFinder . get ( ) ,
& WordFinder : : finished ,
this ,
& ArticleRequest : : individualWordFinished ,
Qt : : QueuedConnection ) ;
2010-03-30 13:41:14 +00:00
currentSplittedWordStart = - 1 ;
currentSplittedWordEnd = currentSplittedWordStart ;
firstCompoundWasFound = false ;
compoundSearchNextStep ( false ) ;
continueMatching = true ;
}
if ( ! continueMatching )
footer + = " </body></html> " ;
2009-04-17 13:51:50 +00:00
{
2023-03-26 06:46:27 +00:00
appendDataSlice ( footer . data ( ) , footer . size ( ) ) ;
2009-04-17 13:51:50 +00:00
}
2023-03-26 06:46:27 +00:00
if ( continueMatching )
2010-03-30 13:41:14 +00:00
update ( ) ;
else
finish ( ) ;
}
void ArticleRequest : : compoundSearchNextStep ( bool lastSearchSucceeded )
{
if ( ! lastSearchSucceeded )
{
// Last search was unsuccessful. First, emit what we had.
string footer ;
2010-05-29 20:50:16 +00:00
if ( lastGoodCompoundResult . size ( ) ) // We have something to append
2010-03-30 13:41:14 +00:00
{
2022-01-15 07:29:20 +00:00
// GD_DPRINTF( "Appending\n" );
2010-03-30 13:41:14 +00:00
if ( ! firstCompoundWasFound )
{
// Append the beginning
2022-12-24 22:01:50 +00:00
footer + = R " (<div class= " gdstemmedsuggestion " ><span class= " gdstemmedsuggestion_head " >) " +
2010-03-30 13:41:14 +00:00
Html : : escape ( tr ( " Compound expressions: " ) . toUtf8 ( ) . data ( ) ) +
" </span><span class= \" gdstemmedsuggestion_body \" > " ;
firstCompoundWasFound = true ;
}
else
{
// Append the separator
footer + = " / " ;
}
2010-05-29 20:50:16 +00:00
footer + = linkWord ( lastGoodCompoundResult ) ;
lastGoodCompoundResult . clear ( ) ;
2010-03-30 13:41:14 +00:00
}
// Then, start a new search for the next word, if possible
if ( currentSplittedWordStart > = splittedWords . first . size ( ) - 2 )
{
// The last word was the last possible to start from
if ( firstCompoundWasFound )
footer + = " </span> " ;
2010-04-01 09:08:51 +00:00
// Now add links to all the individual words. They conclude the result.
2022-12-24 22:01:50 +00:00
footer + = R " (<div class= " gdstemmedsuggestion " ><span class= " gdstemmedsuggestion_head " >) " +
2010-04-01 09:08:51 +00:00
Html : : escape ( tr ( " Individual words: " ) . toUtf8 ( ) . data ( ) ) +
2013-07-16 13:59:56 +00:00
" </span><span class= \" gdstemmedsuggestion_body \" " ;
if ( splittedWords . first [ 0 ] . isRightToLeft ( ) )
footer + = " dir= \" rtl \" " ;
footer + = " > " ;
2010-04-01 09:08:51 +00:00
footer + = escapeSpacing ( splittedWords . second [ 0 ] ) ;
for ( int x = 0 ; x < splittedWords . first . size ( ) ; + + x )
{
footer + = linkWord ( splittedWords . first [ x ] ) ;
footer + = escapeSpacing ( splittedWords . second [ x + 1 ] ) ;
}
footer + = " </span> " ;
2010-03-30 13:41:14 +00:00
footer + = " </body></html> " ;
appendToData ( footer ) ;
finish ( ) ;
return ;
}
if ( footer . size ( ) )
{
appendToData ( footer ) ;
update ( ) ;
}
// Advance to the next word and start from looking up two words
+ + currentSplittedWordStart ;
currentSplittedWordEnd = currentSplittedWordStart + 1 ;
}
else
{
// Last lookup succeeded -- see if we can try the larger sequence
if ( currentSplittedWordEnd < splittedWords . first . size ( ) - 1 )
{
// We can, indeed.
+ + currentSplittedWordEnd ;
}
else
{
// We can't. Emit what we have and start over.
+ + currentSplittedWordEnd ; // So we could use the same code for result
// emitting
// Initiate new lookup
compoundSearchNextStep ( false ) ;
return ;
}
}
// Build the compound sequence
currentSplittedWordCompound = makeSplittedWordCompound ( ) ;
// Look it up
2022-01-15 07:29:20 +00:00
// GD_DPRINTF( "Looking up %s\n", qPrintable( currentSplittedWordCompound ) );
2010-03-30 13:41:14 +00:00
2015-02-25 17:03:08 +00:00
stemmedWordFinder - > expressionMatch ( currentSplittedWordCompound , activeDicts , 40 , // Would one be enough? Leave 40 to be safe.
Dictionary : : SuitableForCompoundSearching ) ;
2010-03-30 13:41:14 +00:00
}
QString ArticleRequest : : makeSplittedWordCompound ( )
{
QString result ;
result . clear ( ) ;
for ( int x = currentSplittedWordStart ; x < = currentSplittedWordEnd ; + + x )
{
result . append ( splittedWords . first [ x ] ) ;
if ( x < currentSplittedWordEnd )
{
2023-04-28 15:44:21 +00:00
result . append ( splittedWords . second [ x + 1 ] . simplified ( ) ) ;
2010-03-30 13:41:14 +00:00
}
}
return result ;
2009-04-17 13:51:50 +00:00
}
2010-03-30 13:41:14 +00:00
void ArticleRequest : : individualWordFinished ( )
{
WordFinder : : SearchResults const & results = stemmedWordFinder - > getResults ( ) ;
if ( results . size ( ) )
{
2023-04-29 02:35:56 +00:00
wstring source = Folding : : applySimpleCaseOnly ( currentSplittedWordCompound ) ;
2010-03-30 13:41:14 +00:00
2010-05-29 20:50:16 +00:00
bool hadSomething = false ;
2010-03-30 13:41:14 +00:00
for ( unsigned x = 0 ; x < results . size ( ) ; + + x )
2010-05-29 20:50:16 +00:00
{
if ( results [ x ] . second )
2015-02-25 17:03:08 +00:00
{
// Spelling suggestion match found. No need to continue.
hadSomething = true ;
lastGoodCompoundResult = currentSplittedWordCompound ;
break ;
}
// Prefix match found. Check if the aliases are acceptable.
2010-05-29 20:50:16 +00:00
2023-04-29 02:35:56 +00:00
wstring result ( Folding : : applySimpleCaseOnly ( results [ x ] . first ) ) ;
2010-05-29 20:50:16 +00:00
if ( source . size ( ) < = result . size ( ) & & result . compare ( 0 , source . size ( ) , source ) = = 0 )
2010-03-30 13:41:14 +00:00
{
2010-05-29 20:50:16 +00:00
// The resulting string begins with the source one
hadSomething = true ;
if ( source . size ( ) = = result . size ( ) )
{
// Got the match. No need to continue.
lastGoodCompoundResult = currentSplittedWordCompound ;
break ;
}
2010-03-30 13:41:14 +00:00
}
2010-05-29 20:50:16 +00:00
}
if ( hadSomething )
{
compoundSearchNextStep ( true ) ;
return ;
}
2010-03-30 13:41:14 +00:00
}
compoundSearchNextStep ( false ) ;
}
void ArticleRequest : : appendToData ( std : : string const & str )
{
2023-03-26 06:46:27 +00:00
appendDataSlice ( str . data ( ) , str . size ( ) ) ;
2010-03-30 13:41:14 +00:00
}
QPair < ArticleRequest : : Words , ArticleRequest : : Spacings > ArticleRequest : : splitIntoWords ( QString const & input )
{
QPair < Words , Spacings > result ;
QChar const * ptr = input . data ( ) ;
for ( ; ; )
{
QString spacing ;
for ( ; ptr - > unicode ( ) & & ( Folding : : isPunct ( ptr - > unicode ( ) ) | | Folding : : isWhitespace ( ptr - > unicode ( ) ) ) ; + + ptr )
spacing . append ( * ptr ) ;
result . second . append ( spacing ) ;
QString word ;
for ( ; ptr - > unicode ( ) & & ! ( Folding : : isPunct ( ptr - > unicode ( ) ) | | Folding : : isWhitespace ( ptr - > unicode ( ) ) ) ; + + ptr )
word . append ( * ptr ) ;
if ( word . isEmpty ( ) )
break ;
result . first . append ( word ) ;
}
return result ;
}
string ArticleRequest : : linkWord ( QString const & str )
{
2013-05-31 04:20:25 +00:00
QUrl url ;
2010-03-30 13:41:14 +00:00
url . setScheme ( " gdlookup " ) ;
url . setHost ( " localhost " ) ;
2021-11-27 07:17:33 +00:00
url . setPath ( Utils : : Url : : ensureLeadingSlash ( str ) ) ;
2010-03-30 13:41:14 +00:00
string escapedResult = Html : : escape ( str . toUtf8 ( ) . data ( ) ) ;
return string ( " <a href= \" " ) + url . toEncoded ( ) . data ( ) + " \" > " + escapedResult + " </a> " ;
}
std : : string ArticleRequest : : escapeSpacing ( QString const & str )
{
QByteArray spacing = Html : : escape ( str . toUtf8 ( ) . data ( ) ) . c_str ( ) ;
spacing . replace ( " \n " , " <br> " ) ;
return spacing . data ( ) ;
}
2011-12-12 16:52:07 +00:00
void ArticleRequest : : cancel ( )
{
if ( isFinished ( ) )
return ;
if ( ! altSearches . empty ( ) )
{
for ( list < sptr < Dictionary : : WordSearchRequest > > : : iterator i =
altSearches . begin ( ) ; i ! = altSearches . end ( ) ; + + i )
{
( * i ) - > cancel ( ) ;
}
}
if ( ! bodyRequests . empty ( ) )
{
for ( list < sptr < Dictionary : : DataRequest > > : : iterator i =
bodyRequests . begin ( ) ; i ! = bodyRequests . end ( ) ; + + i )
{
( * i ) - > cancel ( ) ;
}
}
if ( stemmedWordFinder . get ( ) ) stemmedWordFinder - > cancel ( ) ;
finish ( ) ;
}