Add input phrase's punctuation suffix to alts

Preferences::sanitizeInputPhrase() transforms an input phrase by
removing its whitespace/punctuation prefix and suffix. Translating a
phrase from X11 primary selection or from clipboard, via mouse-over or
from the command line results in such sanitization. This is useful when
a punctuation mark or a space is selected accidentally alongside a word.
This sanitization can be undesirable, however, when an abbreviated word
is selected. For example: "etc.", "e.g.", "i.e.".

This commit implements searching for the input word with the punctuation
suffix preserved as an alternative form of the sanitized word to show
articles for both. For example, when the word "etc." is translated from
the clipboard, both "ETC" and "etc." articles are displayed.

The punctuation suffix is preserved when the word is passed from the
scan popup to the main window and when the translate line text is
refreshed (e.g. when the current group is changed). The suffix is not
stored in history and favorites (doing so would require file format
changes and possibly substantial code changes, this can be implemented
later if need be).

Trim the input phrase once in ArticleNetworkAccessManager::getResource()
instead of verbose trimming in multiple places in
ArticleMaker::makeDefinitionFor().

Closes #1350.
This commit is contained in:
Igor Kushnir 2021-06-10 19:13:11 +03:00
parent 57c4c33780
commit 60bc05218f
11 changed files with 228 additions and 105 deletions

View file

@ -256,7 +256,8 @@ std::string ArticleMaker::makeNotFoundBody( QString const & word,
return result;
}
sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const & inWord, unsigned groupId,
sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(
Config::InputPhrase const & phrase, unsigned groupId,
QMap< QString, QString > const & contexts,
QSet< QString > const & mutedDicts,
QStringList const & dictIDs , bool ignoreDiacritics ) const
@ -282,9 +283,9 @@ sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const &
break;
}
string header = makeHtmlHeader( inWord.trimmed(), QString(), true );
string header = makeHtmlHeader( phrase.phrase, QString(), true );
return new ArticleRequest( inWord.trimmed(), "",
return new ArticleRequest( phrase, "",
contexts, ftsDicts, header,
-1, true );
}
@ -292,9 +293,9 @@ sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const &
if ( groupId == Instances::Group::HelpGroupId )
{
// This is a special group containing internal welcome/help pages
string result = makeHtmlHeader( inWord, QString(), needExpandOptionalParts );
string result = makeHtmlHeader( phrase.phrase, QString(), needExpandOptionalParts );
if ( inWord == tr( "Welcome!" ) )
if ( phrase.phrase == tr( "Welcome!" ) )
{
result += tr(
"<h3 align=\"center\">Welcome to <b>GoldenDict</b>!</h3>"
@ -312,7 +313,7 @@ sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const &
).toUtf8().data();
}
else
if ( inWord == tr( "Working with popup" ) )
if ( phrase.phrase == tr( "Working with popup" ) )
{
result += ( tr( "<h3 align=\"center\">Working with the popup</h3>"
@ -333,7 +334,7 @@ sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const &
else
{
// Not found
return makeNotFoundTextFor( inWord, "help" );
return makeNotFoundTextFor( phrase.phrase, "help" );
}
result += "</body></html>";
@ -362,7 +363,7 @@ sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const &
std::vector< sptr< Dictionary::Class > > const & activeDicts =
activeGroup ? activeGroup->dictionaries : dictionaries;
string header = makeHtmlHeader( inWord.trimmed(),
string header = makeHtmlHeader( phrase.phrase,
activeGroup && activeGroup->icon.size() ?
activeGroup->icon : QString(),
needExpandOptionalParts );
@ -378,13 +379,13 @@ sptr< Dictionary::DataRequest > ArticleMaker::makeDefinitionFor(QString const &
QString::fromStdString( activeDicts[ x ]->getId() ) ) )
unmutedDicts.push_back( activeDicts[ x ] );
return new ArticleRequest( inWord.trimmed(), activeGroup ? activeGroup->name : "",
return new ArticleRequest( phrase, activeGroup ? activeGroup->name : "",
contexts, unmutedDicts, header,
collapseBigArticles ? articleLimitSize : -1,
needExpandOptionalParts, ignoreDiacritics );
}
else
return new ArticleRequest( inWord.trimmed(), activeGroup ? activeGroup->name : "",
return new ArticleRequest( phrase, activeGroup ? activeGroup->name : "",
contexts, activeDicts, header,
collapseBigArticles ? articleLimitSize : -1,
needExpandOptionalParts, ignoreDiacritics );
@ -466,12 +467,12 @@ bool ArticleMaker::adjustFilePath( QString & fileName )
//////// ArticleRequest
ArticleRequest::ArticleRequest(
QString const & word_, QString const & group_,
Config::InputPhrase const & phrase, QString const & group_,
QMap< QString, QString > const & contexts_,
vector< sptr< Dictionary::Class > > const & activeDicts_,
string const & header,
int sizeLimit, bool needExpandOptionalParts_, bool ignoreDiacritics_ ):
word( word_ ), group( group_ ), contexts( contexts_ ),
word( phrase.phrase ), group( group_ ), contexts( contexts_ ),
activeDicts( activeDicts_ ),
altsDone( false ), bodyDone( false ), foundAnyDefinitions( false ),
closePrevSpan( false )
@ -479,6 +480,9 @@ ArticleRequest::ArticleRequest(
, needExpandOptionalParts( needExpandOptionalParts_ )
, ignoreDiacritics( ignoreDiacritics_ )
{
if ( !phrase.punctuationSuffix.isEmpty() )
alts.insert( gd::toWString( phrase.phraseWithSuffix() ) );
// No need to lock dataMutex on construction
hasAnyData = true;

View file

@ -8,6 +8,7 @@
#include <QMap>
#include <set>
#include <list>
#include "config.hh"
#include "dictionary.hh"
#include "instances.hh"
#include "wordfinder.hh"
@ -41,7 +42,7 @@ public:
/// choice of the stylesheet file.
void setDisplayStyle( QString const &, QString const & addonStyle );
/// Looks up the given word within the given group, and creates a full html
/// Looks up the given phrase within the given group, and creates a full html
/// page text containing its definition.
/// The result is returned as Dictionary::DataRequest just like dictionaries
/// themselves do. The difference is that the result is a complete html page
@ -50,7 +51,7 @@ public:
/// the keys are dictionary ids.
/// If mutedDicts is not empty, the search would be limited only to those
/// dictionaries in group which aren't listed there.
sptr< Dictionary::DataRequest > makeDefinitionFor( QString const & word, unsigned groupId,
sptr< Dictionary::DataRequest > makeDefinitionFor( Config::InputPhrase const & phrase, unsigned groupId,
QMap< QString, QString > const & contexts,
QSet< QString > const & mutedDicts =
QSet< QString >(),
@ -129,7 +130,7 @@ class ArticleRequest: public Dictionary::DataRequest
public:
ArticleRequest( QString const & word, QString const & group,
ArticleRequest( Config::InputPhrase const & phrase, QString const & group,
QMap< QString, QString > const & contexts,
std::vector< sptr< Dictionary::Class > > const & activeDicts,
std::string const & header,

View file

@ -388,9 +388,10 @@ sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource(
if ( Qt4x5::Url::queryItemValue( url, "blank" ) == "1" )
return articleMaker.makeEmptyPage();
bool groupIsValid = false;
Config::InputPhrase phrase { Qt4x5::Url::queryItemValue( url, "word" ).trimmed(),
Qt4x5::Url::queryItemValue( url, "punctuation_suffix" ) };
QString word = Qt4x5::Url::queryItemValue( url, "word" );
bool groupIsValid = false;
unsigned group = Qt4x5::Url::queryItemValue( url, "group" ).toUInt( &groupIsValid );
QString dictIDs = Qt4x5::Url::queryItemValue( url, "dictionaries" );
@ -398,7 +399,7 @@ sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource(
{
// Individual dictionaries set from full-text search
QStringList dictIDList = dictIDs.split( "," );
return articleMaker.makeDefinitionFor( word, 0, QMap< QString, QString >(), QSet< QString >(), dictIDList );
return articleMaker.makeDefinitionFor( phrase, 0, QMap< QString, QString >(), QSet< QString >(), dictIDList );
}
// See if we have some dictionaries muted
@ -429,8 +430,8 @@ sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource(
bool ignoreDiacritics = Qt4x5::Url::queryItemValue( url, "ignore_diacritics" ) == "1";
if ( groupIsValid && word.size() ) // Require group and word to be passed
return articleMaker.makeDefinitionFor( word, group, contexts, mutedDicts, QStringList(), ignoreDiacritics );
if ( groupIsValid && phrase.isValid() ) // Require group and phrase to be passed
return articleMaker.makeDefinitionFor( phrase, group, contexts, mutedDicts, QStringList(), ignoreDiacritics );
}
if ( ( url.scheme() == "bres" || url.scheme() == "gdau" || url.scheme() == "gdvideo" || url.scheme() == "gico" ) &&

View file

@ -352,7 +352,7 @@ ArticleView::~ArticleView()
#endif
}
void ArticleView::showDefinition( QString const & word, unsigned group,
void ArticleView::showDefinition( Config::InputPhrase const & phrase, unsigned group,
QString const & scrollTo,
Contexts const & contexts_ )
{
@ -364,7 +364,9 @@ void ArticleView::showDefinition( QString const & word, unsigned group,
req.setScheme( "gdlookup" );
req.setHost( "localhost" );
Qt4x5::Url::addQueryItem( req, "word", word );
Qt4x5::Url::addQueryItem( req, "word", phrase.phrase );
if ( !phrase.punctuationSuffix.isEmpty() )
Qt4x5::Url::addQueryItem( req, "punctuation_suffix", phrase.punctuationSuffix );
Qt4x5::Url::addQueryItem( req, "group", QString::number( group ) );
if( cfg.preferences.ignoreDiacritics )
Qt4x5::Url::addQueryItem( req, "ignore_diacritics", "1" );
@ -401,7 +403,7 @@ void ArticleView::showDefinition( QString const & word, unsigned group,
// Update both histories (pages history and headwords history)
saveHistoryUserData();
emit sendWordToHistory( word );
emit sendWordToHistory( phrase.phrase );
// Any search opened is probably irrelevant now
closeSearch();
@ -417,6 +419,13 @@ void ArticleView::showDefinition( QString const & word, unsigned group,
ui.definition->setCursor( Qt::WaitCursor );
}
void ArticleView::showDefinition( QString const & word, unsigned group,
QString const & scrollTo,
Contexts const & contexts_ )
{
showDefinition( Config::InputPhrase::fromPhrase( word ), group, scrollTo, contexts_ );
}
void ArticleView::showDefinition( QString const & word, QStringList const & dictIDs,
QRegExp const & searchRegExp, unsigned group,
bool ignoreDiacritics )
@ -1668,6 +1677,13 @@ QString ArticleView::getTitle()
return ui.definition->page()->mainFrame()->title();
}
Config::InputPhrase ArticleView::getPhrase() const
{
const QUrl url = ui.definition->url();
return { Qt4x5::Url::queryItemValue( url, "word" ),
Qt4x5::Url::queryItemValue( url, "punctuation_suffix" ) };
}
void ArticleView::print( QPrinter * printer ) const
{
ui.definition->print( printer );
@ -2085,9 +2101,9 @@ void ArticleView::audioPlayerError( QString const & message )
void ArticleView::pasteTriggered()
{
QString text = cfg.preferences.sanitizeInputPhrase( QApplication::clipboard()->text() );
Config::InputPhrase phrase = cfg.preferences.sanitizeInputPhrase( QApplication::clipboard()->text() );
if ( text.size() )
if ( phrase.isValid() )
{
unsigned groupId = getGroup( ui.definition->url() );
if ( groupId == 0 )
@ -2096,7 +2112,7 @@ void ArticleView::pasteTriggered()
// so let's try the currently selected group.
groupId = groupComboBox->getCurrentGroup();
}
showDefinition( text, groupId, getCurrentArticle() );
showDefinition( phrase, groupId, getCurrentArticle() );
}
}

View file

@ -96,6 +96,10 @@ public:
/// contexts is an optional map of context values to be passed for dictionaries.
/// The only values to pass here are ones obtained from showDefinitionInNewTab()
/// signal or none at all.
void showDefinition( Config::InputPhrase const & phrase, unsigned group,
QString const & scrollTo = QString(),
Contexts const & contexts = Contexts() );
void showDefinition( QString const & word, unsigned group,
QString const & scrollTo = QString(),
Contexts const & contexts = Contexts() );
@ -161,6 +165,9 @@ public:
/// Returns current article's title
QString getTitle();
/// Returns the phrase translated by the current article.
Config::InputPhrase getPhrase() const;
/// Prints current article
void print( QPrinter * ) const;

View file

@ -153,15 +153,31 @@ ScanPopupWindowFlags spwfFromInt( int id )
return SPWF_default;
}
QString Preferences::sanitizeInputPhrase( QString const & inputPhrase ) const
InputPhrase Preferences::sanitizeInputPhrase( QString const & inputPhrase ) const
{
InputPhrase result;
if( limitInputPhraseLength && inputPhrase.size() > inputPhraseLengthLimit )
{
gdWarning( "Ignoring an input phrase %d symbols long. The configured maximum input phrase length is %d symbols.",
inputPhrase.size(), inputPhraseLengthLimit );
return QString();
return result;
}
return gd::toQString( Folding::trimWhitespaceOrPunct( gd::toWString( inputPhrase ) ) ).simplified();
const QString withPunct = inputPhrase.simplified();
result.phrase = gd::toQString( Folding::trimWhitespaceOrPunct( gd::toWString( withPunct ) ) );
if ( !result.isValid() )
return result; // The suffix of an invalid input phrase must be empty.
const int prefixSize = withPunct.indexOf( result.phrase.at(0) );
const int suffixSize = withPunct.size() - prefixSize - result.phrase.size();
Q_ASSERT( suffixSize >= 0 );
Q_ASSERT( withPunct.size() - suffixSize - 1
== withPunct.lastIndexOf( result.phrase.at( result.phrase.size() - 1 ) ) );
if ( suffixSize != 0 )
result.punctuationSuffix = withPunct.right( suffixSize );
return result;
}
Preferences::Preferences():

View file

@ -11,6 +11,7 @@
#include <QDateTime>
#include <QKeySequence>
#include <QSet>
#include <QMetaType>
#include "cpp_features.hh"
#include "ex.hh"
@ -243,6 +244,30 @@ enum ScanPopupWindowFlags
};
ScanPopupWindowFlags spwfFromInt( int id );
struct InputPhrase
{
static InputPhrase fromPhrase( QString const & phrase )
{
return { phrase, QString() };
}
bool isValid() const { return !phrase.isEmpty(); }
QString phraseWithSuffix() const { return phrase + punctuationSuffix; }
QString phrase;
QString punctuationSuffix;
};
inline bool operator == ( InputPhrase const & a, InputPhrase const & b )
{
return a.phrase == b.phrase && a.punctuationSuffix == b.punctuationSuffix;
}
inline bool operator != ( InputPhrase const & a, InputPhrase const & b )
{
return !( a == b );
}
/// Various user preferences
struct Preferences
{
@ -323,7 +348,7 @@ struct Preferences
bool limitInputPhraseLength;
int inputPhraseLengthLimit;
QString sanitizeInputPhrase( QString const & inputPhrase ) const;
InputPhrase sanitizeInputPhrase( QString const & inputPhrase ) const;
unsigned short maxDictionaryRefsInContextMenu;
#ifndef Q_WS_X11
@ -802,5 +827,6 @@ QString getNetworkCacheDir() throw();
}
#endif
Q_DECLARE_METATYPE( Config::InputPhrase )
#endif

View file

@ -142,6 +142,8 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
QThreadPool::globalInstance()->start( new InitSSLRunnable );
#endif
qRegisterMetaType< Config::InputPhrase >();
#ifndef NO_EPWING_SUPPORT
Epwing::initialize();
#endif
@ -957,11 +959,7 @@ void MainWindow::updateSearchPaneAndBar( bool searchInDock )
updateGroupList();
applyWordsZoomLevel();
if ( cfg.preferences.searchInDock )
translateLine->setText( text );
else
translateBox->setText( text, false );
setTranslateBoxTextAndKeepSuffix( text, DisablePopup );
focusTranslateLine();
}
@ -981,7 +979,7 @@ void MainWindow::mousePressEvent( QMouseEvent *event)
QString str = QApplication::clipboard()->text(subtype,
QClipboard::Selection);
translateLine->setText(str);
setTranslateBoxTextAndClearSuffix( str, NoPopupChange );
QKeyEvent ev(QEvent::KeyPress, Qt::Key_Enter,
Qt::NoModifier);
@ -1468,8 +1466,8 @@ void MainWindow::makeScanPopup()
connect( scanPopup.get(), SIGNAL(editGroupRequested( unsigned ) ),
this, SLOT(editDictionaries( unsigned )), Qt::QueuedConnection );
connect( scanPopup.get(), SIGNAL(sendWordToMainWindow( QString const & ) ),
this, SLOT(wordReceived( QString const & )), Qt::QueuedConnection );
connect( scanPopup.get(), SIGNAL(sendPhraseToMainWindow( Config::InputPhrase const & ) ),
this, SLOT(phraseReceived( Config::InputPhrase const & )), Qt::QueuedConnection );
connect( this, SIGNAL( setExpandOptionalParts( bool ) ),
scanPopup.get(), SIGNAL( setViewExpandMode( bool ) ) );
@ -2063,7 +2061,7 @@ void MainWindow::editDictionaries( unsigned editDictionaryGroup )
Config::save( cfg );
translateInputChanged( translateLine->text() );
updateSuggestionList();
}
}
@ -2244,7 +2242,7 @@ void MainWindow::currentGroupChanged( QString const & )
// Update word search results
translateBox->setPopupEnabled( false );
translateInputChanged( translateLine->text() );
updateSuggestionList();
translateInputFinished( false );
updateCurrentGroupProperty();
@ -2275,6 +2273,17 @@ void MainWindow::updateCurrentGroupProperty()
}
void MainWindow::translateInputChanged( QString const & newValue )
{
updateSuggestionList( newValue );
translateBoxSuffix = QString();
}
void MainWindow::updateSuggestionList()
{
updateSuggestionList( translateLine->text() );
}
void MainWindow::updateSuggestionList( QString const & newValue )
{
// If there's some status bar message present, clear it since it may be
// about the previous search that has failed.
@ -2312,17 +2321,22 @@ void MainWindow::translateInputChanged( QString const & newValue )
wordFinder.prefixMatch( req, getActiveDicts() );
}
void MainWindow::translateInputFinished( bool checkModifiers, QString const & dictID )
void MainWindow::translateInputFinished( bool checkModifiers )
{
QString word = Folding::unescapeWildcardSymbols( translateLine->text() );
respondToTranslationRequest( { word, translateBoxSuffix }, checkModifiers );
}
if ( word.size() )
void MainWindow::respondToTranslationRequest( Config::InputPhrase const & phrase,
bool checkModifiers, QString const & dictID )
{
if ( phrase.isValid() )
{
Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
if ( checkModifiers && ( mods & (Qt::ControlModifier | Qt::ShiftModifier) ) )
addNewTab();
showTranslationFor( word, 0, dictID );
showTranslationFor( phrase, 0, dictID );
if ( cfg.preferences.searchInDock )
{
@ -2334,6 +2348,20 @@ void MainWindow::translateInputFinished( bool checkModifiers, QString const & di
}
}
void MainWindow::setTranslateBoxTextAndKeepSuffix( QString const & text, TranslateBoxPopup popupAction )
{
if( popupAction == TranslateBoxPopup::NoPopupChange || cfg.preferences.searchInDock )
translateLine->setText( text );
else
translateBox->setText( text, popupAction == TranslateBoxPopup::EnablePopup );
}
void MainWindow::setTranslateBoxTextAndClearSuffix( QString const & text, TranslateBoxPopup popupAction )
{
setTranslateBoxTextAndKeepSuffix( text, popupAction );
translateBoxSuffix = QString();
}
void MainWindow::handleEsc()
{
ArticleView *view = getCurrentArticleView();
@ -2370,8 +2398,7 @@ void MainWindow::applyMutedDictionariesState()
{
translateBox->setPopupEnabled( false );
// Redo the current search request
translateInputChanged( translateLine->text() );
updateSuggestionList();
ArticleView *view = getCurrentArticleView();
@ -2724,10 +2751,7 @@ void MainWindow::typingEvent( QString const & t )
if( translateLine->isEnabled() )
{
translateLine->setFocus();
if ( cfg.preferences.searchInDock )
translateLine->setText( t );
else
translateBox->setText( t, true );
setTranslateBoxTextAndClearSuffix( t, EnablePopup );
translateLine->setCursorPosition( t.size() );
}
}
@ -2745,17 +2769,13 @@ void MainWindow::showHistoryItem( QString const & word )
history.enableAdd( false );
if ( cfg.preferences.searchInDock )
translateLine->setText( Folding::escapeWildcardSymbols( word ) );
else
translateBox->setText( Folding::escapeWildcardSymbols( word ), false );
setTranslateBoxTextAndClearSuffix( Folding::escapeWildcardSymbols( word ), DisablePopup );
showTranslationFor( word );
history.enableAdd( cfg.preferences.storeHistory );
}
void MainWindow::showTranslationFor( QString const & inWord,
void MainWindow::showTranslationFor( Config::InputPhrase const & phrase,
unsigned inGroup,
QString const & dictID )
{
@ -2767,14 +2787,14 @@ void MainWindow::showTranslationFor( QString const & inWord,
( groupInstances.empty() ? 0 :
groupInstances[ groupList->currentIndex() ].id );
view->showDefinition( inWord, group, dictID );
view->showDefinition( phrase, group, dictID );
updatePronounceAvailability();
updateFoundInDictsList();
// Add to history
addWordToHistory( inWord );
addWordToHistory( phrase.phrase );
updateBackForwardButtons();
@ -2867,6 +2887,11 @@ void MainWindow::showTranslationFor( QString const & inWord,
//ui.tabWidget->setTabText( ui.tabWidget->indexOf(ui.tab), inWord.trimmed() );
}
void MainWindow::showTranslationFor( QString const & word )
{
showTranslationFor( Config::InputPhrase::fromPhrase( word ) );
}
void MainWindow::showTranslationFor( QString const & inWord,
QStringList const & dictIDs,
QRegExp const & searchRegExp,
@ -3639,9 +3664,7 @@ void MainWindow::on_rescanFiles_triggered()
makeScanPopup();
installHotKeys();
// Reload suggestion list
QString word = translateLine->text();
translateInputChanged( word );
updateSuggestionList();
}
void MainWindow::on_alwaysOnTop_triggered( bool checked )
@ -3871,18 +3894,25 @@ ArticleView * MainWindow::getCurrentArticleView()
return 0;
}
void MainWindow::phraseReceived( Config::InputPhrase const & phrase )
{
toggleMainWindow( true );
setTranslateBoxTextAndKeepSuffix( Folding::escapeWildcardSymbols( phrase.phrase ), NoPopupChange );
translateBoxSuffix = phrase.punctuationSuffix;
respondToTranslationRequest( phrase, false );
}
void MainWindow::wordReceived( const QString & word)
{
toggleMainWindow( true );
translateLine->setText( Folding::escapeWildcardSymbols( word ) );
translateInputFinished( false );
phraseReceived( Config::InputPhrase::fromPhrase( word ) );
}
void MainWindow::headwordReceived( const QString & word, const QString & ID )
{
toggleMainWindow( true );
translateLine->setText( Folding::escapeWildcardSymbols( word ) );
translateInputFinished( false, QString( "gdfrom-" )+ ID );
toggleMainWindow( true );
setTranslateBoxTextAndClearSuffix( Folding::escapeWildcardSymbols( word ), NoPopupChange );
respondToTranslationRequest( Config::InputPhrase::fromPhrase( word ),
false, "gdfrom-" + ID );
}
void MainWindow::updateFavoritesMenu()
@ -4520,8 +4550,7 @@ void MainWindow::foundDictsContextMenuRequested( const QPoint &pos )
void MainWindow::sendWordToInputLine( const QString & word )
{
translateLine->clear();
translateLine->setText( word );
setTranslateBoxTextAndClearSuffix( word, NoPopupChange );
}
void MainWindow::storeResourceSavePath( const QString & newPath )
@ -4824,12 +4853,7 @@ void MainWindow::headwordFromFavorites( QString const & headword,
}
// Show headword without lost of focus on Favorites tree
if ( cfg.preferences.searchInDock )
translateLine->setText( Folding::escapeWildcardSymbols( headword ) );
else
translateBox->setText( Folding::escapeWildcardSymbols( headword ), false );
setTranslateBoxTextAndClearSuffix( Folding::escapeWildcardSymbols( headword ), DisablePopup );
showTranslationFor(headword );
}

View file

@ -82,6 +82,7 @@ public slots:
void messageFromAnotherInstanceReceived( QString const & );
void showStatusBarMessage ( QString const &, int, QPixmap const & );
void phraseReceived( Config::InputPhrase const & );
void wordReceived( QString const & );
void headwordReceived( QString const &, QString const & );
void setExpandMode( bool expand );
@ -158,6 +159,7 @@ private:
WordList * wordList;
QLineEdit * translateLine;
QString translateBoxSuffix; ///< A punctuation suffix that corresponds to translateLine's text.
WordFinder wordFinder;
@ -257,6 +259,16 @@ private:
QString unescapeTabHeader( QString const & header );
void respondToTranslationRequest( Config::InputPhrase const & phrase,
bool checkModifiers, QString const & dictID = QString() );
void updateSuggestionList();
void updateSuggestionList( QString const & text );
enum TranslateBoxPopup { NoPopupChange, EnablePopup, DisablePopup };
void setTranslateBoxTextAndKeepSuffix( QString const & text, TranslateBoxPopup popupAction );
void setTranslateBoxTextAndClearSuffix( QString const & text, TranslateBoxPopup popupAction );
private slots:
void hotKeyActivated( int );
@ -358,7 +370,7 @@ private slots:
void currentGroupChanged( QString const & );
void translateInputChanged( QString const & );
void translateInputFinished( bool checkModifiers = true, QString const & dictID = QString() );
void translateInputFinished( bool checkModifiers = true );
/// Closes any opened search in the article view, and focuses the translateLine/close main window to tray.
void handleEsc();
@ -393,8 +405,9 @@ private slots:
void mutedDictionariesChanged();
void showTranslationFor( QString const &, unsigned inGroup = 0,
void showTranslationFor( Config::InputPhrase const &, unsigned inGroup = 0,
QString const & dictID = QString() );
void showTranslationFor( QString const & );
void showTranslationFor( QString const &, QStringList const & dictIDs,
QRegExp const & searchRegExp, bool ignoreDiacritics );

View file

@ -492,9 +492,9 @@ void ScanPopup::translateWordFromClipboard(QClipboard::Mode m)
void ScanPopup::translateWord( QString const & word )
{
QString str = pendingInputWord = cfg.preferences.sanitizeInputPhrase( word );
pendingInputPhrase = cfg.preferences.sanitizeInputPhrase( word );
if ( !str.size() )
if ( !pendingInputPhrase.isValid() )
return; // Nothing there
// In case we had any timers engaged before, cancel them now.
@ -505,7 +505,7 @@ void ScanPopup::translateWord( QString const & word )
emit hideScanFlag();
#endif
inputWord = str;
inputPhrase = pendingInputPhrase;
engagePopup( false,
#ifdef Q_OS_WIN
true // We only focus popup under Windows when activated via Ctrl+C+C
@ -556,18 +556,18 @@ void ScanPopup::mouseHovered( QString const & str, bool forcePopup )
void ScanPopup::handleInputWord( QString const & str, bool forcePopup )
{
QString sanitizedStr = cfg.preferences.sanitizeInputPhrase( str );
Config::InputPhrase sanitizedPhrase = cfg.preferences.sanitizeInputPhrase( str );
if ( isVisible() && sanitizedStr == inputWord )
if ( isVisible() && sanitizedPhrase == inputPhrase )
{
// Attempt to translate the same word we already have shown in scan popup.
// Ignore it, as it is probably a spurious mouseover event.
return;
}
pendingInputWord = sanitizedStr;
pendingInputPhrase = sanitizedPhrase;
if ( !pendingInputWord.size() )
if ( !pendingInputPhrase.isValid() )
{
if ( cfg.preferences.scanPopupAltMode )
{
@ -581,7 +581,7 @@ void ScanPopup::handleInputWord( QString const & str, bool forcePopup )
#ifdef HAVE_X11
if ( cfg.preferences.showScanFlag ) {
inputWord = pendingInputWord;
inputPhrase = pendingInputPhrase;
emit showScanFlag( forcePopup );
return;
}
@ -600,7 +600,7 @@ void ScanPopup::handleInputWord( QString const & str, bool forcePopup )
return;
}
inputWord = pendingInputWord;
inputPhrase = pendingInputPhrase;
engagePopup( forcePopup );
}
@ -616,7 +616,7 @@ void ScanPopup::engagePopup( bool forcePopup, bool giveFocus )
if( cfg.preferences.scanToMainWindow && !forcePopup )
{
// Send translated word to main window istead of show popup
emit sendWordToMainWindow( inputWord );
emit sendPhraseToMainWindow( inputPhrase );
return;
}
@ -712,13 +712,15 @@ void ScanPopup::engagePopup( bool forcePopup, bool giveFocus )
/// Too large strings make window expand which is probably not what user
/// wants
ui.translateBox->setText( inputWord, false );
ui.translateBox->setText( inputPhrase.phrase, false );
translateBoxSuffix = inputPhrase.punctuationSuffix;
showTranslationFor( inputWord );
showTranslationFor( inputPhrase );
}
QString ScanPopup::elideInputWord()
{
QString const & inputWord = inputPhrase.phrase;
return inputWord.size() > 32 ? inputWord.mid( 0, 32 ) + "..." : inputWord;
}
@ -749,7 +751,7 @@ void ScanPopup::currentGroupChanged( QString const & )
if ( isVisible() )
{
translateInputChanged( ui.translateBox->translateLine()->text() );
updateSuggestionList();
translateInputFinished();
}
@ -758,10 +760,21 @@ void ScanPopup::currentGroupChanged( QString const & )
void ScanPopup::wordListItemActivated( QListWidgetItem * item )
{
showTranslationFor( item->text() );
showTranslationFor( Config::InputPhrase::fromPhrase( item->text() ) );
}
void ScanPopup::translateInputChanged( QString const & text )
{
updateSuggestionList( text );
translateBoxSuffix = QString();
}
void ScanPopup::updateSuggestionList()
{
updateSuggestionList( ui.translateBox->translateLine()->text() );
}
void ScanPopup::updateSuggestionList( QString const & text )
{
mainStatusBar->clearMessage();
ui.translateBox->wordList()->setCurrentItem( 0, QItemSelectionModel::Clear );
@ -791,20 +804,20 @@ void ScanPopup::translateInputChanged( QString const & text )
void ScanPopup::translateInputFinished()
{
inputWord = ui.translateBox->translateLine()->text().trimmed();
showTranslationFor( inputWord );
inputPhrase = { ui.translateBox->translateLine()->text().trimmed(), translateBoxSuffix };
showTranslationFor( inputPhrase );
}
void ScanPopup::showTranslationFor( QString const & inputWord )
void ScanPopup::showTranslationFor( Config::InputPhrase const & inputPhrase )
{
ui.pronounceButton->hide();
unsigned groupId = ui.groupList->getCurrentGroup();
definition->showDefinition( inputWord, groupId );
definition->showDefinition( inputPhrase, groupId );
definition->focus();
// Add to history
emit sendWordToHistory( inputWord.trimmed() );
emit sendWordToHistory( inputPhrase.phrase.trimmed() );
}
vector< sptr< Dictionary::Class > > const & ScanPopup::getActiveDicts()
@ -1124,7 +1137,7 @@ void ScanPopup::altModeExpired()
void ScanPopup::altModePoll()
{
if ( !pendingInputWord.size() )
if ( !pendingInputPhrase.isValid() )
{
altModePollingTimer.stop();
altModeExpirationTimer.stop();
@ -1135,7 +1148,7 @@ void ScanPopup::altModePoll()
altModePollingTimer.stop();
altModeExpirationTimer.stop();
inputWord = pendingInputWord;
inputPhrase = pendingInputPhrase;
engagePopup( false );
}
}
@ -1232,9 +1245,7 @@ void ScanPopup::updateDictionaryBar()
void ScanPopup::mutedDictionariesChanged()
{
// Update suggestion list
translateInputChanged( ui.translateBox->translateLine()->text() );
updateSuggestionList();
if ( dictionaryBar.toggleViewAction()->isChecked() )
definition->updateMutedContents();
}
@ -1248,7 +1259,7 @@ void ScanPopup::on_sendWordButton_clicked()
definition->closeSearch();
hideWindow();
}
emit sendWordToMainWindow( definition->getTitle() );
emit sendPhraseToMainWindow( definition->getPhrase() );
}
void ScanPopup::on_sendWordToFavoritesButton_clicked()

View file

@ -59,7 +59,7 @@ signals:
/// Forwarded from the dictionary bar, so that main window could act on this.
void editGroupRequested( unsigned id );
/// Send word to main window
void sendWordToMainWindow( QString const & word );
void sendPhraseToMainWindow( Config::InputPhrase const & phrase );
/// Close opened menus when window hide
void closeMenu();
/// Signals to set expand optional parts mode (retranslation from/to MainWindow and dictionary bar)
@ -133,7 +133,8 @@ private:
ArticleView * definition;
QAction escapeAction, switchExpandModeAction, focusTranslateLineAction;
QAction openSearchAction;
QString pendingInputWord, inputWord;
Config::InputPhrase pendingInputPhrase, inputPhrase;
QString translateBoxSuffix; ///< A punctuation suffix that corresponds to translateBox's text.
WordFinder wordFinder;
Config::Events configEvents;
DictionaryBar dictionaryBar;
@ -183,7 +184,10 @@ private:
void updateBackForwardButtons();
void showTranslationFor( QString const & inputWord );
void showTranslationFor( Config::InputPhrase const & inputPhrase );
void updateSuggestionList();
void updateSuggestionList( QString const & text );
private slots: