goldendict-ng/config.cc

2362 lines
83 KiB
C++
Raw Normal View History

2012-02-20 21:47:14 +00:00
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "config.hh"
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
#include "folding.hh"
#include "wstring_qt.hh"
#include <QDir>
#include <QFile>
#include <QtXml>
#include "gddebug.hh"
#include <stdint.h>
#ifdef Q_OS_WIN32
#include "shlobj.h"
#endif
#include "atomic_rename.hh"
#include "utils.hh"
#include <QStandardPaths>
#if defined( HAVE_X11 )
// Whether XDG Base Directory specification might be followed.
// Only Qt5 builds are supported, as Qt4 doesn't provide all functions needed
// to get XDG Base Directory compliant locations.
#define XDG_BASE_DIRECTORY_COMPLIANCE
#endif
namespace Config {
namespace
{
#ifdef XDG_BASE_DIRECTORY_COMPLIANCE
const char xdgSubdirName[] = "goldendict";
QDir getDataDir()
{
QDir dir = QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation );
dir.mkpath( xdgSubdirName );
if ( !dir.cd( xdgSubdirName ) )
throw exCantUseDataDir();
return dir;
}
#endif
QString portableHomeDirPath()
{
return QCoreApplication::applicationDirPath() + "/portable";
}
QDir getHomeDir()
{
if ( isPortableVersion() )
return QDir( portableHomeDirPath() );
QDir result;
result = QDir::home();
#ifdef Q_OS_WIN32
if ( result.cd( "Application Data/GoldenDict" ) )
return result;
char const * pathInHome = "GoldenDict";
result = QDir::fromNativeSeparators( QString::fromWCharArray( _wgetenv( L"APPDATA" ) ) );
#else
char const * pathInHome = ".goldendict";
#ifdef XDG_BASE_DIRECTORY_COMPLIANCE
// check if an old config dir is present, otherwise use standards-compliant location
if ( !result.exists( pathInHome ) )
{
result.setPath( QStandardPaths::writableLocation( QStandardPaths::ConfigLocation ) );
pathInHome = xdgSubdirName;
}
#endif
#endif
result.mkpath( pathInHome );
if ( !result.cd( pathInHome ) )
throw exCantUseHomeDir();
return result;
}
}
2014-04-01 17:00:13 +00:00
ProxyServer::ProxyServer(): enabled( false ), useSystemProxy( false ), type( Socks5 ), port( 3128 )
{
}
AnkiConnectServer::AnkiConnectServer(): enabled( false ), host("127.0.0.1"), port( 8765 )
{
}
HotKey::HotKey(): modifiers( 0 ), key1( 0 ), key2( 0 )
{
}
// Does anyone know how to separate modifiers from the keycode? We'll
// use our own mask.
uint32_t const keyMask = 0x01FFFFFF;
HotKey::HotKey( QKeySequence const & seq ):
modifiers( seq[ 0 ] & ~keyMask ),
key1( seq[ 0 ] & keyMask ),
key2( seq[ 1 ] & keyMask )
{
}
QKeySequence HotKey::toKeySequence() const
{
int v2 = key2 ? ( key2 | modifiers ): 0;
return QKeySequence( key1 | modifiers, v2 );
}
bool InternalPlayerBackend::anyAvailable()
{
#if defined( MAKE_FFMPEG_PLAYER ) || defined( MAKE_QTMULTIMEDIA_PLAYER )
return true;
#else
return false;
#endif
}
InternalPlayerBackend InternalPlayerBackend::defaultBackend()
{
#if defined( MAKE_FFMPEG_PLAYER )
return ffmpeg();
#elif defined( MAKE_QTMULTIMEDIA_PLAYER )
return qtmultimedia();
#else
return InternalPlayerBackend( QString() );
#endif
}
QStringList InternalPlayerBackend::nameList()
{
QStringList result;
#ifdef MAKE_FFMPEG_PLAYER
result.push_back( ffmpeg().uiName() );
#endif
#ifdef MAKE_QTMULTIMEDIA_PLAYER
result.push_back( qtmultimedia().uiName() );
#endif
return result;
}
bool InternalPlayerBackend::isFfmpeg() const
{
#ifdef MAKE_FFMPEG_PLAYER
return *this == ffmpeg();
#else
return false;
#endif
}
bool InternalPlayerBackend::isQtmultimedia() const
{
#ifdef MAKE_QTMULTIMEDIA_PLAYER
return *this == qtmultimedia();
#else
return false;
#endif
}
Allow customizing unpinned scan popup window flags on X11 with Qt5 My tests in many desktop environments and window managers indicate that no single configuration works perfectly in all environments. There are also behavior differences between Qt::Popup and Qt::Tool flags, which are not exactly bugs, so I suppose users might subjectively prefer different options. Customizing the flags allows the user to prevent unpinned scan popup window flickering with Qt5 on Linux. In a way adding these options fixes issue #645, which is: the scan popup window blinks rapidly, barely noticeably in some applications, such as Calibre ebook-viewer and Chromium. In this case the scan popup window usually ends up hidden when selection ends, unless it was finished with a jerk. I have tested the new options in 9 desktop environments and window managers: at least one configuration for each eliminates #645 and makes the scan popup window work the same as with Qt4 in this regard: the popup window remains visible, text in the popup's translation line keeps up with the text selection in the external application, and the selected text is being translated on the fly. Moreover, for each tested DE/WM, at least one configuration makes the scan popup window work perfectly as far as I am concerned. This issue was partially worked around with a 200ms scan popup delay timer in the recent commit 58e41fe3ceb769cab37608e36637044e597ba1f8 for the duplicate issue #854. However the timer solution is incomplete because it requires the user to select text quickly and without delays. If global mouse selection does not change for 200ms while the left mouse button is held down, the user will likely not see the scan popup when (s)he finishes selection, and will have to try selecting again - hopefully faster this time. The 200ms delay is no longer critically important after this commit, but it is still beneficial: the lookup query changes less often, which in turn reduces article definition update frequency. So the delay improves the UI (perhaps subjectively) and performance.
2018-04-09 17:50:23 +00:00
ScanPopupWindowFlags spwfFromInt( int id )
{
if( id == SPWF_Popup )
return SPWF_Popup;
if( id == SPWF_Tool )
return SPWF_Tool;
if( id != SPWF_default )
gdWarning( "Invalid ScanPopup unpinned window flags: %d\n", id );
return SPWF_default;
}
InputPhrase Preferences::sanitizeInputPhrase( QString const & inputPhrase ) const
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
{
InputPhrase result;
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
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 result;
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
}
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;
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
}
Preferences::Preferences():
newTabsOpenAfterCurrentOne( false ),
newTabsOpenInBackground( true ),
hideSingleTab( false ),
2011-11-02 23:37:50 +00:00
mruTabOrder ( false ),
hideMenubar( false ),
enableTrayIcon( true ),
startToTray( false ),
closeToTray( true ),
autoStart( false ),
doubleClickTranslates( true ),
selectWordBySingleClick( false ),
Add "Automatically scroll to target article" option When a user clicks on a link in a dictionary or requests translation of a word by double-clicking or translates selection via the context menu, at first the article from the highest-priority dictionary is at the top. Then, after approximately one second, the article from the dictionary, out of which the translation was requested, becomes current and the view scrolls down to this article placing it on top, hiding articles from the dictionaries above it. Such application behavior is inconvenient in some workflows so that the user manually navigates to the top dictionary translation when this automatic scrolling happens. For example: a user has English->Russian dictionaries and English->English dictionaries. The English->Russian dictionaries are higher up in the dictionary order because they provide easier/faster to understand translations. Some rare words and phrases are missing from the English->Russian dictionaries however. Thus the user occasionally reads the English explanation of a word/phrase. When the user double-clicks on a word or follows a link in the English->English dictionary article, she would rather see translations from the preferable English->Russian dictionaries. The new option allows to disable automatic scrolling and ensure that articles from higher-priority dictionaries are visible. The option doesn't affect backward/forward navigation via arrow buttons or Alt+Arrow shortcuts: these still scroll to the stored vertical position among articles. This remaining automatic scrolling happens much faster, is not a problem for the described use case and hopefully for other use cases.
2019-01-11 13:50:40 +00:00
autoScrollToTargetArticle( true ),
escKeyHidesMainWindow( false ),
alwaysOnTop ( false ),
searchInDock ( false ),
enableMainWindowHotkey( true ),
mainWindowHotkey( QKeySequence( "Ctrl+F11,F11" ) ),
enableClipboardHotkey( true ),
2009-05-24 19:04:24 +00:00
clipboardHotkey( QKeySequence( "Ctrl+C,C" ) ),
enableScanPopup( true ),
startWithScanPopupOn( false ),
enableScanPopupModifiers( false ),
scanPopupModifiers( 0 ),
scanPopupAltMode( false ),
scanPopupAltModeSecs( 3 ),
ignoreOwnClipboardChanges( false ),
Allow customizing unpinned scan popup window flags on X11 with Qt5 My tests in many desktop environments and window managers indicate that no single configuration works perfectly in all environments. There are also behavior differences between Qt::Popup and Qt::Tool flags, which are not exactly bugs, so I suppose users might subjectively prefer different options. Customizing the flags allows the user to prevent unpinned scan popup window flickering with Qt5 on Linux. In a way adding these options fixes issue #645, which is: the scan popup window blinks rapidly, barely noticeably in some applications, such as Calibre ebook-viewer and Chromium. In this case the scan popup window usually ends up hidden when selection ends, unless it was finished with a jerk. I have tested the new options in 9 desktop environments and window managers: at least one configuration for each eliminates #645 and makes the scan popup window work the same as with Qt4 in this regard: the popup window remains visible, text in the popup's translation line keeps up with the text selection in the external application, and the selected text is being translated on the fly. Moreover, for each tested DE/WM, at least one configuration makes the scan popup window work perfectly as far as I am concerned. This issue was partially worked around with a 200ms scan popup delay timer in the recent commit 58e41fe3ceb769cab37608e36637044e597ba1f8 for the duplicate issue #854. However the timer solution is incomplete because it requires the user to select text quickly and without delays. If global mouse selection does not change for 200ms while the left mouse button is held down, the user will likely not see the scan popup when (s)he finishes selection, and will have to try selecting again - hopefully faster this time. The 200ms delay is no longer critically important after this commit, but it is still beneficial: the lookup query changes less often, which in turn reduces article definition update frequency. So the delay improves the UI (perhaps subjectively) and performance.
2018-04-09 17:50:23 +00:00
scanPopupUnpinnedWindowFlags( SPWF_default ),
scanPopupUnpinnedBypassWMHint( false ),
scanToMainWindow( false ),
ignoreDiacritics( false ),
2022-04-23 08:15:23 +00:00
ignorePunctuation( false ),
#ifdef HAVE_X11
showScanFlag( false ),
#endif
pronounceOnLoadMain( false ),
pronounceOnLoadPopup( false ),
useInternalPlayer( InternalPlayerBackend::anyAvailable() ),
internalPlayerBackend( InternalPlayerBackend::defaultBackend() ),
2009-04-30 19:57:25 +00:00
checkForNewReleases( true ),
disallowContentFromOtherSites( false ),
enableWebPlugins( false ),
hideGoldenDictHeader( false ),
maxNetworkCacheSize( 50 ),
clearNetworkCacheOnExit( true ),
zoomFactor( 1 ),
2014-06-23 15:11:15 +00:00
helpZoomFactor( 1 ),
2012-02-16 14:56:25 +00:00
wordsZoomLevel( 0 ),
maxStringsInHistory( 500 ),
storeHistory( 1 ),
alwaysExpandOptionalParts( false )
, historyStoreInterval( 0 )
, favoritesStoreInterval( 0 )
, confirmFavoritesDeletion( true )
, collapseBigArticles( false )
, articleSizeLimit( 2000 )
, limitInputPhraseLength( false )
, inputPhraseLengthLimit( 1000 )
, maxDictionaryRefsInContextMenu ( 20 )
#ifndef Q_WS_X11
, trackClipboardChanges( false )
#endif
, synonymSearchEnabled( true )
{
}
Chinese::Chinese():
enable( false ),
enableSCToTWConversion( true ),
enableSCToHKConversion( true ),
enableTCToSCConversion( true )
{
}
Romaji::Romaji():
enable( false ),
enableHepburn( true ),
enableNihonShiki( false ),
enableKunreiShiki( false ),
enableHiragana( true ),
enableKatakana( true )
{
}
Group * Class::getGroup( unsigned id )
{
2012-12-10 12:49:45 +00:00
for( int x = 0; x < groups.size(); x++ )
if( groups.at( x ).id == id )
return &groups[ x ];
return 0;
}
Group const * Class::getGroup( unsigned id ) const
{
2012-12-10 12:49:45 +00:00
for( int x = 0; x < groups.size(); x++ )
if( groups.at( x ).id == id )
return &groups.at( x );
return 0;
}
void Events::signalMutedDictionariesChanged()
{
emit mutedDictionariesChanged();
}
namespace {
MediaWikis makeDefaultMediaWikis( bool enable )
{
MediaWikis mw;
mw.push_back( MediaWiki( "ae6f89aac7151829681b85f035d54e48", "English Wikipedia", "https://en.wikipedia.org/w", enable, "" ) );
mw.push_back( MediaWiki( "affcf9678e7bfe701c9b071f97eccba3", "English Wiktionary", "https://en.wiktionary.org/w", false, "" ) );
mw.push_back( MediaWiki( "8e0c1c2b6821dab8bdba8eb869ca7176", "Russian Wikipedia", "https://ru.wikipedia.org/w", false, "" ) );
mw.push_back( MediaWiki( "b09947600ae3902654f8ad4567ae8567", "Russian Wiktionary", "https://ru.wiktionary.org/w", false, "" ) );
mw.push_back( MediaWiki( "a8a66331a1242ca2aeb0b4aed361c41d", "German Wikipedia", "https://de.wikipedia.org/w", false, "" ) );
mw.push_back( MediaWiki( "21c64bca5ec10ba17ff19f3066bc962a", "German Wiktionary", "https://de.wiktionary.org/w", false, "" ) );
mw.push_back( MediaWiki( "96957cb2ad73a20c7a1d561fc83c253a", "Portuguese Wikipedia", "https://pt.wikipedia.org/w", false, "" ) );
mw.push_back( MediaWiki( "ed4c3929196afdd93cc08b9a903aad6a", "Portuguese Wiktionary", "https://pt.wiktionary.org/w", false, "" ) );
mw.push_back( MediaWiki( "f3b4ec8531e52ddf5b10d21e4577a7a2", "Greek Wikipedia", "https://el.wikipedia.org/w", false, "" ) );
mw.push_back( MediaWiki( "5d45232075d06e002dea72fe3e137da1", "Greek Wiktionary", "https://el.wiktionary.org/w", false, "" ) );
return mw;
}
WebSites makeDefaultWebSites()
{
WebSites ws;
ws.push_back( WebSite( "b88cb2898e634c6638df618528284c2d", "Google En-En (Oxford)", "https://www.google.com/search?q=define:%GDWORD%&hl=en", false, "", true ) );
ws.push_back( WebSite( "f376365a0de651fd7505e7e5e683aa45", "Urban Dictionary", "https://www.urbandictionary.com/define.php?term=%GDWORD%", false, "", true ) );
ws.push_back( WebSite( "324ca0306187df7511b26d3847f4b07c", "Multitran (En)", "https://multitran.ru/c/m.exe?CL=1&l1=1&s=%GD1251%", false, "", true ) );
ws.push_back( WebSite( "924db471b105299c82892067c0f10787", "Lingvo (En-Ru)", "http://lingvopro.abbyyonline.com/en/Search/en-ru/%GDWORD%", false, "", true ) );
ws.push_back( WebSite( "087a6d65615fb047f4c80eef0a9465db", "Michaelis (Pt-En)", "http://michaelis.uol.com.br/moderno/ingles/index.php?lingua=portugues-ingles&palavra=%GDISO1%", false, "", true ) );
return ws;
}
2014-04-30 12:55:53 +00:00
DictServers makeDefaultDictServers()
{
DictServers ds;
return ds;
}
Programs makeDefaultPrograms()
{
Programs programs;
// The following list doesn't make a lot of sense under Windows
#ifndef Q_OS_WIN
2012-12-09 17:29:27 +00:00
programs.push_back( Program( false, Program::Audio, "428b4c2b905ef568a43d9a16f59559b0", "Festival", "festival --tts", "" ) );
programs.push_back( Program( false, Program::Audio, "2cf8b3a60f27e1ac812de0b57c148340", "Espeak", "espeak %GDWORD%", "" ) );
programs.push_back( Program( false, Program::Html, "4f898f7582596cea518c6b0bfdceb8b3", "Manpages", "man -a --html=/bin/cat %GDWORD%", "" ) );
#endif
return programs;
}
/// Sets option to true of false if node is "1" or "0" respectively, or leaves
/// it intact if it's neither "1" nor "0".
void applyBoolOption( bool & option, QDomNode const & node )
{
QString value = node.toElement().text();
if ( value == "1" )
option = true;
else
if ( value == "0" )
option = false;
}
Group loadGroup( QDomElement grp, unsigned * nextId = 0 )
{
Group g;
if ( grp.hasAttribute( "id" ) )
g.id = grp.attribute( "id" ).toUInt();
else
g.id = nextId ? (*nextId)++ : 0;
g.name = grp.attribute( "name" );
g.icon = grp.attribute( "icon" );
2017-05-05 14:39:51 +00:00
g.favoritesFolder = grp.attribute( "favoritesFolder" );
if ( !grp.attribute( "iconData" ).isEmpty() )
g.iconData = QByteArray::fromBase64( grp.attribute( "iconData" ).toLatin1() );
if ( !grp.attribute( "shortcut" ).isEmpty() )
g.shortcut = QKeySequence::fromString( grp.attribute( "shortcut" ) );
QDomNodeList dicts = grp.elementsByTagName( "dictionary" );
2022-04-05 12:53:25 +00:00
for( int y = 0; y < dicts.length(); ++y )
g.dictionaries.push_back( DictionaryRef( dicts.item( y ).toElement().text(),
dicts.item( y ).toElement().attribute( "name" ) ) );
QDomNode muted = grp.namedItem( "mutedDictionaries" );
dicts = muted.toElement().elementsByTagName( "mutedDictionary" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < dicts.length(); ++x )
g.mutedDictionaries.insert( dicts.item( x ).toElement().text() );
dicts = muted.toElement().elementsByTagName( "popupMutedDictionary" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < dicts.length(); ++x )
g.popupMutedDictionaries.insert( dicts.item( x ).toElement().text() );
return g;
}
MutedDictionaries loadMutedDictionaries( QDomNode mutedDictionaries )
{
MutedDictionaries result;
if ( !mutedDictionaries.isNull() )
{
QDomNodeList nl = mutedDictionaries.toElement().
elementsByTagName( "mutedDictionary" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
result.insert( nl.item( x ).toElement().text() );
}
return result;
}
void saveMutedDictionaries( QDomDocument & dd, QDomElement & muted,
MutedDictionaries const & mutedDictionaries )
{
for( MutedDictionaries::const_iterator i = mutedDictionaries.begin();
i != mutedDictionaries.end(); ++i )
{
QDomElement dict = dd.createElement( "mutedDictionary" );
muted.appendChild( dict );
QDomText value = dd.createTextNode( *i );
dict.appendChild( value );
}
}
}
2022-01-09 08:35:07 +00:00
Class load()
{
QString configName = getConfigFileName();
bool loadFromTemplate = false;
if ( !QFile::exists( configName ) )
{
// Make the default config, save it and return it
Class c;
#ifdef Q_OS_LINUX
if ( QDir( "/usr/share/stardict/dic" ).exists() )
c.paths.push_back( Path( "/usr/share/stardict/dic", true ) );
if ( QDir( "/usr/share/dictd" ).exists() )
c.paths.push_back( Path( "/usr/share/dictd", true ) );
if ( QDir( "/usr/share/opendict/dictionaries" ).exists() )
c.paths.push_back( Path( "/usr/share/opendict/dictionaries", true ) );
if ( QDir( "/usr/share/goldendict-wordnet" ).exists() )
c.paths.push_back( Path( "/usr/share/goldendict-wordnet", true ) );
if ( QDir( "/usr/share/WyabdcRealPeopleTTS" ).exists() )
c.soundDirs.push_back( SoundDir( "/usr/share/WyabdcRealPeopleTTS", "WyabdcRealPeopleTTS" ) );
2009-04-09 14:15:01 +00:00
if ( QDir( "/usr/share/myspell/dicts" ).exists() )
c.hunspell.dictionariesPath = "/usr/share/myspell/dicts";
#endif
#ifdef Q_OS_WIN32
// get path to Program Files
wchar_t buf[ MAX_PATH ];
SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES, NULL, 0, buf );
QString pathToProgramFiles = QString::fromWCharArray( buf );
if ( pathToProgramFiles.isEmpty() )
pathToProgramFiles = "C:\\Program Files";
if ( QDir( pathToProgramFiles + "\\StarDict\\dic" ).exists() )
c.paths.push_back( Path( pathToProgramFiles + "\\StarDict\\dic", true ) );
if ( QDir( pathToProgramFiles + "\\StarDict\\WyabdcRealPeopleTTS" ).exists() )
c.soundDirs.push_back( SoundDir( pathToProgramFiles + "\\StarDict\\WyabdcRealPeopleTTS", "WyabdcRealPeopleTTS" ) );
else
if ( QDir( pathToProgramFiles + "\\WyabdcRealPeopleTTS" ).exists() )
c.soundDirs.push_back( SoundDir( pathToProgramFiles + "\\WyabdcRealPeopleTTS", "WyabdcRealPeopleTTS" ) );
// #### "C:/Program Files" is bad! will not work for German Windows etc.
// #### should be replaced to system path
// if ( QDir( "C:/Program Files/StarDict/dic" ).exists() )
// c.paths.push_back( Path( "C:/Program Files/StarDict/dic", true ) );
//
// if ( QDir( "C:/Program Files/StarDict/WyabdcRealPeopleTTS" ).exists() )
// c.soundDirs.push_back( SoundDir( "C:/Program Files/StarDict/WyabdcRealPeopleTTS", "WyabdcRealPeopleTTS" ) );
// else
// if ( QDir( "C:/Program Files/WyabdcRealPeopleTTS" ).exists() )
// c.soundDirs.push_back( SoundDir( "C:/Program Files/WyabdcRealPeopleTTS", "WyabdcRealPeopleTTS" ) );
#endif
#ifndef Q_OS_WIN32
c.preferences.audioPlaybackProgram = "mplayer";
#endif
QString possibleMorphologyPath = getProgramDataDir() + "/content/morphology";
if ( QDir( possibleMorphologyPath ).exists() )
c.hunspell.dictionariesPath = possibleMorphologyPath;
c.mediawikis = makeDefaultMediaWikis( true );
c.webSites = makeDefaultWebSites();
2014-04-30 12:55:53 +00:00
c.dictServers = makeDefaultDictServers();
c.programs = makeDefaultPrograms();
// Check if we have a template config file. If we do, load it instead
configName = getProgramDataDir() + "/content/defconfig";
loadFromTemplate = QFile( configName ).exists();
if ( !loadFromTemplate )
{
save( c );
return c;
}
}
2012-12-10 14:14:13 +00:00
getStylesDir();
QFile configFile( configName );
if ( !configFile.open( QFile::ReadOnly ) )
throw exCantReadConfigFile();
QDomDocument dd;
QString errorStr;
int errorLine, errorColumn;
if ( !loadFromTemplate )
{
// Load the config as usual
if ( !dd.setContent( &configFile, false, &errorStr, &errorLine, &errorColumn ) )
{
GD_DPRINTF( "Error: %s at %d,%d\n", errorStr.toLocal8Bit().constData(), errorLine, errorColumn );
throw exMalformedConfigFile();
}
}
else
{
// We need to replace all %PROGRAMDIR% with the program data dir
QByteArray data = configFile.readAll();
data.replace( "%PROGRAMDIR%", getProgramDataDir().toUtf8() );
QBuffer bufferedData( &data );
if ( !dd.setContent( &bufferedData, false, &errorStr, &errorLine, &errorColumn ) )
{
GD_DPRINTF( "Error: %s at %d,%d\n", errorStr.toLocal8Bit().constData(), errorLine, errorColumn );
throw exMalformedConfigFile();
}
}
configFile.close();
QDomNode root = dd.namedItem( "config" );
Class c;
QDomNode paths = root.namedItem( "paths" );
if ( !paths.isNull() )
{
QDomNodeList nl = paths.toElement().elementsByTagName( "path" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
c.paths.push_back(
Path( nl.item( x ).toElement().text(),
nl.item( x ).toElement().attribute( "recursive" ) == "1" ) );
}
QDomNode soundDirs = root.namedItem( "sounddirs" );
if ( !soundDirs.isNull() )
{
QDomNodeList nl = soundDirs.toElement().elementsByTagName( "sounddir" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
c.soundDirs.push_back(
SoundDir( nl.item( x ).toElement().text(),
nl.item( x ).toElement().attribute( "name" ),
nl.item( x ).toElement().attribute( "icon" ) ) );
}
QDomNode dictionaryOrder = root.namedItem( "dictionaryOrder" );
if ( !dictionaryOrder.isNull() )
c.dictionaryOrder = loadGroup( dictionaryOrder.toElement() );
QDomNode inactiveDictionaries = root.namedItem( "inactiveDictionaries" );
if ( !inactiveDictionaries.isNull() )
c.inactiveDictionaries = loadGroup( inactiveDictionaries.toElement() );
QDomNode groups = root.namedItem( "groups" );
if ( !groups.isNull() )
{
c.groups.nextId = groups.toElement().attribute( "nextId", "1" ).toUInt();
QDomNodeList nl = groups.toElement().elementsByTagName( "group" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
{
QDomElement grp = nl.item( x ).toElement();
c.groups.push_back( loadGroup( grp, &c.groups.nextId ) );
}
}
2009-04-09 14:15:01 +00:00
QDomNode hunspell = root.namedItem( "hunspell" );
if ( !hunspell.isNull() )
{
c.hunspell.dictionariesPath = hunspell.toElement().attribute( "dictionariesPath" );
QDomNodeList nl = hunspell.toElement().elementsByTagName( "enabled" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
2009-04-09 14:15:01 +00:00
c.hunspell.enabledDictionaries.push_back( nl.item( x ).toElement().text() );
}
QDomNode transliteration = root.namedItem( "transliteration" );
2009-05-11 23:37:18 +00:00
if ( !transliteration.isNull() )
{
applyBoolOption( c.transliteration.enableRussianTransliteration,
transliteration.namedItem( "enableRussianTransliteration" ) );
2009-05-11 23:37:18 +00:00
applyBoolOption( c.transliteration.enableGermanTransliteration,
transliteration.namedItem( "enableGermanTransliteration" ) );
applyBoolOption( c.transliteration.enableGreekTransliteration,
transliteration.namedItem( "enableGreekTransliteration" ) );
2013-01-18 19:21:56 +00:00
applyBoolOption( c.transliteration.enableBelarusianTransliteration,
transliteration.namedItem( "enableBelarusianTransliteration" ) );
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
QDomNode chinese = transliteration.namedItem( "chinese" );
if ( !chinese.isNull() )
{
applyBoolOption( c.transliteration.chinese.enable, chinese.namedItem( "enable" ) );
applyBoolOption( c.transliteration.chinese.enableSCToTWConversion, chinese.namedItem( "enableSCToTWConversion" ) );
applyBoolOption( c.transliteration.chinese.enableSCToHKConversion, chinese.namedItem( "enableSCToHKConversion" ) );
applyBoolOption( c.transliteration.chinese.enableTCToSCConversion, chinese.namedItem( "enableTCToSCConversion" ) );
}
#endif
QDomNode romaji = transliteration.namedItem( "romaji" );
if ( !romaji.isNull() )
{
applyBoolOption( c.transliteration.romaji.enable, romaji.namedItem( "enable" ) );
applyBoolOption( c.transliteration.romaji.enableHepburn, romaji.namedItem( "enableHepburn" ) );
applyBoolOption( c.transliteration.romaji.enableNihonShiki, romaji.namedItem( "enableNihonShiki" ) );
applyBoolOption( c.transliteration.romaji.enableKunreiShiki, romaji.namedItem( "enableKunreiShiki" ) );
applyBoolOption( c.transliteration.romaji.enableHiragana, romaji.namedItem( "enableHiragana" ) );
applyBoolOption( c.transliteration.romaji.enableKatakana, romaji.namedItem( "enableKatakana" ) );
}
}
QDomNode forvo = root.namedItem( "forvo" );
if ( !forvo.isNull() )
{
applyBoolOption( c.forvo.enable,
forvo.namedItem( "enable" ) );
c.forvo.apiKey = forvo.namedItem( "apiKey" ).toElement().text();
c.forvo.languageCodes = forvo.namedItem( "languageCodes" ).toElement().text();
}
else
c.forvo.languageCodes = "en, ru"; // Default demo values
QDomNode programs = root.namedItem( "programs" );
if ( !programs.isNull() )
{
QDomNodeList nl = programs.toElement().elementsByTagName( "program" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
{
QDomElement pr = nl.item( x ).toElement();
Program p;
p.id = pr.attribute( "id" );
p.name = pr.attribute( "name" );
p.commandLine = pr.attribute( "commandLine" );
p.enabled = ( pr.attribute( "enabled" ) == "1" );
p.type = (Program::Type)( pr.attribute( "type" ).toInt() );
2012-12-07 11:59:59 +00:00
p.iconFilename = pr.attribute( "icon" );
c.programs.push_back( p );
}
}
else
{
c.programs = makeDefaultPrograms();
}
QDomNode mws = root.namedItem( "mediawikis" );
if ( !mws.isNull() )
{
QDomNodeList nl = mws.toElement().elementsByTagName( "mediawiki" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
{
QDomElement mw = nl.item( x ).toElement();
MediaWiki w;
w.id = mw.attribute( "id" );
w.name = mw.attribute( "name" );
w.url = mw.attribute( "url" );
w.enabled = ( mw.attribute( "enabled" ) == "1" );
w.icon = mw.attribute( "icon" );
c.mediawikis.push_back( w );
}
}
else
{
// When upgrading, populate the list with some choices, but don't enable
// anything.
c.mediawikis = makeDefaultMediaWikis( false );
}
QDomNode wss = root.namedItem( "websites" );
if ( !wss.isNull() )
{
QDomNodeList nl = wss.toElement().elementsByTagName( "website" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
{
QDomElement ws = nl.item( x ).toElement();
WebSite w;
w.id = ws.attribute( "id" );
w.name = ws.attribute( "name" );
w.url = ws.attribute( "url" );
w.enabled = ( ws.attribute( "enabled" ) == "1" );
2012-12-03 12:48:31 +00:00
w.iconFilename = ws.attribute( "icon" );
w.inside_iframe = ( ws.attribute( "inside_iframe", "1" ) == "1" );
c.webSites.push_back( w );
}
}
else
{
// Upgrading
c.webSites = makeDefaultWebSites();
}
2014-04-30 12:55:53 +00:00
QDomNode dss = root.namedItem( "dictservers" );
if ( !dss.isNull() )
{
QDomNodeList nl = dss.toElement().elementsByTagName( "server" );
2022-04-05 12:53:25 +00:00
for( int x = 0; x < nl.length(); ++x )
2014-04-30 12:55:53 +00:00
{
QDomElement ds = nl.item( x ).toElement();
DictServer d;
d.id = ds.attribute( "id" );
d.name = ds.attribute( "name" );
d.url = ds.attribute( "url" );
d.enabled = ( ds.attribute( "enabled" ) == "1" );
d.databases = ds.attribute( "databases" );
d.strategies = ds.attribute( "strategies" );
2014-04-30 12:55:53 +00:00
d.iconFilename = ds.attribute( "icon" );
c.dictServers.push_back( d );
}
}
else
{
// Upgrading
c.dictServers = makeDefaultDictServers();
}
QDomNode ves = root.namedItem( "voiceEngines" );
if ( !ves.isNull() )
{
QDomNodeList nl = ves.toElement().elementsByTagName( "voiceEngine" );
2022-04-05 12:53:25 +00:00
for ( int x = 0; x < nl.length(); ++x )
{
QDomElement ve = nl.item( x ).toElement();
VoiceEngine v;
v.enabled = ve.attribute( "enabled" ) == "1";
v.id = ve.attribute( "id" );
v.name = ve.attribute( "name" );
v.iconFilename = ve.attribute( "icon" );
v.volume = ve.attribute( "volume", "50" ).toInt();
if( v.volume < 0 || v.volume > 100 )
v.volume = 50;
v.rate = ve.attribute( "rate", "50" ).toInt();
if( v.rate < 0 || v.rate > 100 )
v.rate = 50;
c.voiceEngines.push_back( v );
}
}
c.mutedDictionaries = loadMutedDictionaries( root.namedItem( "mutedDictionaries" ) );
c.popupMutedDictionaries = loadMutedDictionaries( root.namedItem( "popupMutedDictionaries" ) );
QDomNode preferences = root.namedItem( "preferences" );
if ( !preferences.isNull() )
{
c.preferences.interfaceLanguage = preferences.namedItem( "interfaceLanguage" ).toElement().text();
2014-06-25 14:01:11 +00:00
c.preferences.helpLanguage = preferences.namedItem( "helpLanguage" ).toElement().text();
c.preferences.displayStyle = preferences.namedItem( "displayStyle" ).toElement().text();
c.preferences.newTabsOpenAfterCurrentOne = ( preferences.namedItem( "newTabsOpenAfterCurrentOne" ).toElement().text() == "1" );
c.preferences.newTabsOpenInBackground = ( preferences.namedItem( "newTabsOpenInBackground" ).toElement().text() == "1" );
c.preferences.hideSingleTab = ( preferences.namedItem( "hideSingleTab" ).toElement().text() == "1" );
2011-11-02 23:37:50 +00:00
c.preferences.mruTabOrder = ( preferences.namedItem( "mruTabOrder" ).toElement().text() == "1" );
c.preferences.hideMenubar = ( preferences.namedItem( "hideMenubar" ).toElement().text() == "1" );
c.preferences.enableTrayIcon = ( preferences.namedItem( "enableTrayIcon" ).toElement().text() == "1" );
c.preferences.startToTray = ( preferences.namedItem( "startToTray" ).toElement().text() == "1" );
c.preferences.closeToTray = ( preferences.namedItem( "closeToTray" ).toElement().text() == "1" );
c.preferences.autoStart = ( preferences.namedItem( "autoStart" ).toElement().text() == "1" );
c.preferences.alwaysOnTop = ( preferences.namedItem( "alwaysOnTop" ).toElement().text() == "1" );
c.preferences.searchInDock = ( preferences.namedItem( "searchInDock" ).toElement().text() == "1" );
if ( !preferences.namedItem( "webFontFamily" ).isNull() )
c.preferences.webFontFamily = preferences.namedItem( "webFontFamily" ).toElement().text();
if ( !preferences.namedItem( "doubleClickTranslates" ).isNull() )
c.preferences.doubleClickTranslates = ( preferences.namedItem( "doubleClickTranslates" ).toElement().text() == "1" );
if ( !preferences.namedItem( "selectWordBySingleClick" ).isNull() )
c.preferences.selectWordBySingleClick = ( preferences.namedItem( "selectWordBySingleClick" ).toElement().text() == "1" );
Add "Automatically scroll to target article" option When a user clicks on a link in a dictionary or requests translation of a word by double-clicking or translates selection via the context menu, at first the article from the highest-priority dictionary is at the top. Then, after approximately one second, the article from the dictionary, out of which the translation was requested, becomes current and the view scrolls down to this article placing it on top, hiding articles from the dictionaries above it. Such application behavior is inconvenient in some workflows so that the user manually navigates to the top dictionary translation when this automatic scrolling happens. For example: a user has English->Russian dictionaries and English->English dictionaries. The English->Russian dictionaries are higher up in the dictionary order because they provide easier/faster to understand translations. Some rare words and phrases are missing from the English->Russian dictionaries however. Thus the user occasionally reads the English explanation of a word/phrase. When the user double-clicks on a word or follows a link in the English->English dictionary article, she would rather see translations from the preferable English->Russian dictionaries. The new option allows to disable automatic scrolling and ensure that articles from higher-priority dictionaries are visible. The option doesn't affect backward/forward navigation via arrow buttons or Alt+Arrow shortcuts: these still scroll to the stored vertical position among articles. This remaining automatic scrolling happens much faster, is not a problem for the described use case and hopefully for other use cases.
2019-01-11 13:50:40 +00:00
if ( !preferences.namedItem( "autoScrollToTargetArticle" ).isNull() )
c.preferences.autoScrollToTargetArticle = ( preferences.namedItem( "autoScrollToTargetArticle" ).toElement().text() == "1" );
if ( !preferences.namedItem( "escKeyHidesMainWindow" ).isNull() )
c.preferences.escKeyHidesMainWindow = ( preferences.namedItem( "escKeyHidesMainWindow" ).toElement().text() == "1" );
2009-04-30 19:57:25 +00:00
if ( !preferences.namedItem( "zoomFactor" ).isNull() )
c.preferences.zoomFactor = preferences.namedItem( "zoomFactor" ).toElement().text().toDouble();
2014-06-23 15:11:15 +00:00
if ( !preferences.namedItem( "helpZoomFactor" ).isNull() )
c.preferences.helpZoomFactor = preferences.namedItem( "helpZoomFactor" ).toElement().text().toDouble();
if ( !preferences.namedItem( "wordsZoomLevel" ).isNull() )
c.preferences.wordsZoomLevel = preferences.namedItem( "wordsZoomLevel" ).toElement().text().toInt();
applyBoolOption( c.preferences.enableMainWindowHotkey, preferences.namedItem( "enableMainWindowHotkey" ) );
if ( !preferences.namedItem( "mainWindowHotkey" ).isNull() )
c.preferences.mainWindowHotkey = QKeySequence::fromString( preferences.namedItem( "mainWindowHotkey" ).toElement().text() );
applyBoolOption( c.preferences.enableClipboardHotkey, preferences.namedItem( "enableClipboardHotkey" ) );
if ( !preferences.namedItem( "clipboardHotkey" ).isNull() )
c.preferences.clipboardHotkey = QKeySequence::fromString( preferences.namedItem( "clipboardHotkey" ).toElement().text() );
c.preferences.enableScanPopup = ( preferences.namedItem( "enableScanPopup" ).toElement().text() == "1" );
c.preferences.startWithScanPopupOn = ( preferences.namedItem( "startWithScanPopupOn" ).toElement().text() == "1" );
c.preferences.enableScanPopupModifiers = ( preferences.namedItem( "enableScanPopupModifiers" ).toElement().text() == "1" );
c.preferences.scanPopupModifiers = ( preferences.namedItem( "scanPopupModifiers" ).toElement().text().toULong() );
c.preferences.scanPopupAltMode = ( preferences.namedItem( "scanPopupAltMode" ).toElement().text() == "1" );
if ( !preferences.namedItem( "scanPopupAltModeSecs" ).isNull() )
c.preferences.scanPopupAltModeSecs = preferences.namedItem( "scanPopupAltModeSecs" ).toElement().text().toUInt();
c.preferences.ignoreOwnClipboardChanges = ( preferences.namedItem( "ignoreOwnClipboardChanges" ).toElement().text() == "1" );
c.preferences.scanToMainWindow = ( preferences.namedItem( "scanToMainWindow" ).toElement().text() == "1" );
c.preferences.ignoreDiacritics = ( preferences.namedItem( "ignoreDiacritics" ).toElement().text() == "1" );
2022-04-23 08:15:23 +00:00
if( !preferences.namedItem( "ignorePunctuation" ).isNull() )
c.preferences.ignorePunctuation = ( preferences.namedItem( "ignorePunctuation" ).toElement().text() == "1" );
#ifdef HAVE_X11
c.preferences.showScanFlag= ( preferences.namedItem( "showScanFlag" ).toElement().text() == "1" );
#endif
Allow customizing unpinned scan popup window flags on X11 with Qt5 My tests in many desktop environments and window managers indicate that no single configuration works perfectly in all environments. There are also behavior differences between Qt::Popup and Qt::Tool flags, which are not exactly bugs, so I suppose users might subjectively prefer different options. Customizing the flags allows the user to prevent unpinned scan popup window flickering with Qt5 on Linux. In a way adding these options fixes issue #645, which is: the scan popup window blinks rapidly, barely noticeably in some applications, such as Calibre ebook-viewer and Chromium. In this case the scan popup window usually ends up hidden when selection ends, unless it was finished with a jerk. I have tested the new options in 9 desktop environments and window managers: at least one configuration for each eliminates #645 and makes the scan popup window work the same as with Qt4 in this regard: the popup window remains visible, text in the popup's translation line keeps up with the text selection in the external application, and the selected text is being translated on the fly. Moreover, for each tested DE/WM, at least one configuration makes the scan popup window work perfectly as far as I am concerned. This issue was partially worked around with a 200ms scan popup delay timer in the recent commit 58e41fe3ceb769cab37608e36637044e597ba1f8 for the duplicate issue #854. However the timer solution is incomplete because it requires the user to select text quickly and without delays. If global mouse selection does not change for 200ms while the left mouse button is held down, the user will likely not see the scan popup when (s)he finishes selection, and will have to try selecting again - hopefully faster this time. The 200ms delay is no longer critically important after this commit, but it is still beneficial: the lookup query changes less often, which in turn reduces article definition update frequency. So the delay improves the UI (perhaps subjectively) and performance.
2018-04-09 17:50:23 +00:00
c.preferences.scanPopupUnpinnedWindowFlags = spwfFromInt( preferences.namedItem( "scanPopupUnpinnedWindowFlags" ).toElement().text().toInt() );
c.preferences.scanPopupUnpinnedBypassWMHint = ( preferences.namedItem( "scanPopupUnpinnedBypassWMHint" ).toElement().text() == "1" );
c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" );
c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" );
if ( InternalPlayerBackend::anyAvailable() )
{
if ( !preferences.namedItem( "useInternalPlayer" ).isNull() )
c.preferences.useInternalPlayer = ( preferences.namedItem( "useInternalPlayer" ).toElement().text() == "1" );
}
else
c.preferences.useInternalPlayer = false;
if ( !preferences.namedItem( "internalPlayerBackend" ).isNull() )
c.preferences.internalPlayerBackend.setUiName( preferences.namedItem( "internalPlayerBackend" ).toElement().text() );
if ( !preferences.namedItem( "audioPlaybackProgram" ).isNull() )
c.preferences.audioPlaybackProgram = preferences.namedItem( "audioPlaybackProgram" ).toElement().text();
else
c.preferences.audioPlaybackProgram = "mplayer";
QDomNode proxy = preferences.namedItem( "proxyserver" );
if ( !proxy.isNull() )
{
c.preferences.proxyServer.enabled = ( proxy.toElement().attribute( "enabled" ) == "1" );
2014-04-01 17:00:13 +00:00
c.preferences.proxyServer.useSystemProxy = ( proxy.toElement().attribute( "useSystemProxy" ) == "1" );
c.preferences.proxyServer.type = ( ProxyServer::Type ) proxy.namedItem( "type" ).toElement().text().toULong();
c.preferences.proxyServer.host = proxy.namedItem( "host" ).toElement().text();
c.preferences.proxyServer.port = proxy.namedItem( "port" ).toElement().text().toULong();
c.preferences.proxyServer.user = proxy.namedItem( "user" ).toElement().text();
c.preferences.proxyServer.password = proxy.namedItem( "password" ).toElement().text();
2014-04-03 14:21:02 +00:00
c.preferences.proxyServer.systemProxyUser = proxy.namedItem( "systemProxyUser" ).toElement().text();
c.preferences.proxyServer.systemProxyPassword = proxy.namedItem( "systemProxyPassword" ).toElement().text();
}
QDomNode ankiConnectServer = preferences.namedItem( "ankiConnectServer" );
if ( !ankiConnectServer.isNull() )
{
c.preferences.ankiConnectServer.enabled = ( ankiConnectServer.toElement().attribute( "enabled" ) == "1" );
c.preferences.ankiConnectServer.host = ankiConnectServer.namedItem( "host" ).toElement().text();
c.preferences.ankiConnectServer.port = ankiConnectServer.namedItem( "port" ).toElement().text().toULong();
c.preferences.ankiConnectServer.deck = ankiConnectServer.namedItem( "deck" ).toElement().text();
c.preferences.ankiConnectServer.model = ankiConnectServer.namedItem( "model" ).toElement().text();
}
if ( !preferences.namedItem( "checkForNewReleases" ).isNull() )
c.preferences.checkForNewReleases = ( preferences.namedItem( "checkForNewReleases" ).toElement().text() == "1" );
if ( !preferences.namedItem( "disallowContentFromOtherSites" ).isNull() )
c.preferences.disallowContentFromOtherSites = ( preferences.namedItem( "disallowContentFromOtherSites" ).toElement().text() == "1" );
if ( !preferences.namedItem( "enableWebPlugins" ).isNull() )
c.preferences.enableWebPlugins = ( preferences.namedItem( "enableWebPlugins" ).toElement().text() == "1" );
2012-02-16 14:56:25 +00:00
if ( !preferences.namedItem( "hideGoldenDictHeader" ).isNull() )
c.preferences.hideGoldenDictHeader = ( preferences.namedItem( "hideGoldenDictHeader" ).toElement().text() == "1" );
if ( !preferences.namedItem( "maxNetworkCacheSize" ).isNull() )
c.preferences.maxNetworkCacheSize = preferences.namedItem( "maxNetworkCacheSize" ).toElement().text().toInt();
if ( !preferences.namedItem( "clearNetworkCacheOnExit" ).isNull() )
c.preferences.clearNetworkCacheOnExit = ( preferences.namedItem( "clearNetworkCacheOnExit" ).toElement().text() == "1" );
2012-02-16 14:56:25 +00:00
if ( !preferences.namedItem( "maxStringsInHistory" ).isNull() )
c.preferences.maxStringsInHistory = preferences.namedItem( "maxStringsInHistory" ).toElement().text().toUInt() ;
2012-09-10 18:00:29 +00:00
if ( !preferences.namedItem( "storeHistory" ).isNull() )
c.preferences.storeHistory = preferences.namedItem( "storeHistory" ).toElement().text().toUInt() ;
if ( !preferences.namedItem( "alwaysExpandOptionalParts" ).isNull() )
c.preferences.alwaysExpandOptionalParts = preferences.namedItem( "alwaysExpandOptionalParts" ).toElement().text().toUInt() ;
2012-12-10 14:14:13 +00:00
if ( !preferences.namedItem( "addonStyle" ).isNull() )
c.preferences.addonStyle = preferences.namedItem( "addonStyle" ).toElement().text();
if ( !preferences.namedItem( "historyStoreInterval" ).isNull() )
c.preferences.historyStoreInterval = preferences.namedItem( "historyStoreInterval" ).toElement().text().toUInt() ;
if ( !preferences.namedItem( "favoritesStoreInterval" ).isNull() )
c.preferences.favoritesStoreInterval = preferences.namedItem( "favoritesStoreInterval" ).toElement().text().toUInt() ;
if ( !preferences.namedItem( "confirmFavoritesDeletion" ).isNull() )
c.preferences.confirmFavoritesDeletion = ( preferences.namedItem( "confirmFavoritesDeletion" ).toElement().text() == "1" );
if ( !preferences.namedItem( "collapseBigArticles" ).isNull() )
c.preferences.collapseBigArticles = ( preferences.namedItem( "collapseBigArticles" ).toElement().text() == "1" );
if ( !preferences.namedItem( "articleSizeLimit" ).isNull() )
c.preferences.articleSizeLimit = preferences.namedItem( "articleSizeLimit" ).toElement().text().toInt();
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
if ( !preferences.namedItem( "limitInputPhraseLength" ).isNull() )
c.preferences.limitInputPhraseLength = ( preferences.namedItem( "limitInputPhraseLength" ).toElement().text() == "1" );
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
if ( !preferences.namedItem( "inputPhraseLengthLimit" ).isNull() )
c.preferences.inputPhraseLengthLimit = preferences.namedItem( "inputPhraseLengthLimit" ).toElement().text().toInt();
if ( !preferences.namedItem( "maxDictionaryRefsInContextMenu" ).isNull() )
c.preferences.maxDictionaryRefsInContextMenu = preferences.namedItem( "maxDictionaryRefsInContextMenu" ).toElement().text().toUShort();
#ifndef Q_WS_X11
if ( !preferences.namedItem( "trackClipboardChanges" ).isNull() )
c.preferences.trackClipboardChanges = ( preferences.namedItem( "trackClipboardChanges" ).toElement().text() == "1" );
#endif
if ( !preferences.namedItem( "synonymSearchEnabled" ).isNull() )
c.preferences.synonymSearchEnabled = ( preferences.namedItem( "synonymSearchEnabled" ).toElement().text() == "1" );
QDomNode fts = preferences.namedItem( "fullTextSearch" );
if ( !fts.isNull() )
{
if ( !fts.namedItem( "searchMode" ).isNull() )
c.preferences.fts.searchMode = fts.namedItem( "searchMode" ).toElement().text().toInt();
if ( !fts.namedItem( "matchCase" ).isNull() )
c.preferences.fts.matchCase = ( fts.namedItem( "matchCase" ).toElement().text() == "1" );
if ( !fts.namedItem( "maxArticlesPerDictionary" ).isNull() )
c.preferences.fts.maxArticlesPerDictionary = fts.namedItem( "maxArticlesPerDictionary" ).toElement().text().toInt();
if ( !fts.namedItem( "maxDistanceBetweenWords" ).isNull() )
c.preferences.fts.maxDistanceBetweenWords = fts.namedItem( "maxDistanceBetweenWords" ).toElement().text().toInt();
if ( !fts.namedItem( "useMaxArticlesPerDictionary" ).isNull() )
c.preferences.fts.useMaxArticlesPerDictionary = ( fts.namedItem( "useMaxArticlesPerDictionary" ).toElement().text() == "1" );
if ( !fts.namedItem( "useMaxDistanceBetweenWords" ).isNull() )
c.preferences.fts.useMaxDistanceBetweenWords = ( fts.namedItem( "useMaxDistanceBetweenWords" ).toElement().text() == "1" );
if ( !fts.namedItem( "dialogGeometry" ).isNull() )
c.preferences.fts.dialogGeometry = QByteArray::fromBase64( fts.namedItem( "dialogGeometry" ).toElement().text().toLatin1() );
if( !fts.namedItem( "disabledTypes" ).isNull() )
c.preferences.fts.disabledTypes = fts.namedItem( "disabledTypes" ).toElement().text();
if ( !fts.namedItem( "enabled" ).isNull() )
c.preferences.fts.enabled = ( fts.namedItem( "enabled" ).toElement().text() == "1" );
if ( !fts.namedItem( "ignoreWordsOrder" ).isNull() )
c.preferences.fts.ignoreWordsOrder = ( fts.namedItem( "ignoreWordsOrder" ).toElement().text() == "1" );
if ( !fts.namedItem( "ignoreDiacritics" ).isNull() )
c.preferences.fts.ignoreDiacritics = ( fts.namedItem( "ignoreDiacritics" ).toElement().text() == "1" );
if ( !fts.namedItem( "maxDictionarySize" ).isNull() )
c.preferences.fts.maxDictionarySize = fts.namedItem( "maxDictionarySize" ).toElement().text().toUInt();
}
}
c.lastMainGroupId = root.namedItem( "lastMainGroupId" ).toElement().text().toUInt();
c.lastPopupGroupId = root.namedItem( "lastPopupGroupId" ).toElement().text().toUInt();
QDomNode popupWindowState = root.namedItem( "popupWindowState" );
if ( !popupWindowState.isNull() )
c.popupWindowState = QByteArray::fromBase64( popupWindowState.toElement().text().toLatin1() );
QDomNode popupWindowGeometry = root.namedItem( "popupWindowGeometry" );
if ( !popupWindowGeometry.isNull() )
c.popupWindowGeometry = QByteArray::fromBase64( popupWindowGeometry.toElement().text().toLatin1() );
c.pinPopupWindow = ( root.namedItem( "pinPopupWindow" ).toElement().text() == "1" );
c.popupWindowAlwaysOnTop = ( root.namedItem( "popupWindowAlwaysOnTop" ).toElement().text() == "1" );
QDomNode mainWindowState = root.namedItem( "mainWindowState" );
if ( !mainWindowState.isNull() )
c.mainWindowState = QByteArray::fromBase64( mainWindowState.toElement().text().toLatin1() );
QDomNode mainWindowGeometry = root.namedItem( "mainWindowGeometry" );
if ( !mainWindowGeometry.isNull() )
c.mainWindowGeometry = QByteArray::fromBase64( mainWindowGeometry.toElement().text().toLatin1() );
2014-06-23 15:11:15 +00:00
QDomNode helpWindowGeometry = root.namedItem( "helpWindowGeometry" );
if ( !helpWindowGeometry.isNull() )
c.helpWindowGeometry = QByteArray::fromBase64( helpWindowGeometry.toElement().text().toLatin1() );
QDomNode helpSplitterState = root.namedItem( "helpSplitterState" );
if ( !helpSplitterState.isNull() )
c.helpSplitterState = QByteArray::fromBase64( helpSplitterState.toElement().text().toLatin1() );
QDomNode dictInfoGeometry = root.namedItem( "dictInfoGeometry" );
if ( !dictInfoGeometry.isNull() )
c.dictInfoGeometry = QByteArray::fromBase64( dictInfoGeometry.toElement().text().toLatin1() );
QDomNode inspectorGeometry = root.namedItem( "inspectorGeometry" );
if ( !inspectorGeometry.isNull() )
c.inspectorGeometry = QByteArray::fromBase64( inspectorGeometry.toElement().text().toLatin1() );
QDomNode dictionariesDialogGeometry = root.namedItem( "dictionariesDialogGeometry" );
if ( !dictionariesDialogGeometry.isNull() )
c.dictionariesDialogGeometry = QByteArray::fromBase64( dictionariesDialogGeometry.toElement().text().toLatin1() );
QDomNode timeForNewReleaseCheck = root.namedItem( "timeForNewReleaseCheck" );
if ( !timeForNewReleaseCheck.isNull() )
c.timeForNewReleaseCheck = QDateTime::fromString( timeForNewReleaseCheck.toElement().text(),
Qt::ISODate );
c.skippedRelease = root.namedItem( "skippedRelease" ).toElement().text();
c.showingDictBarNames = ( root.namedItem( "showingDictBarNames" ).toElement().text() == "1" );
c.usingSmallIconsInToolbars = ( root.namedItem( "usingSmallIconsInToolbars" ).toElement().text() == "1" );
if ( !root.namedItem( "historyExportPath" ).isNull() )
c.historyExportPath = root.namedItem( "historyExportPath" ).toElement().text();
if ( !root.namedItem( "resourceSavePath" ).isNull() )
c.resourceSavePath = root.namedItem( "resourceSavePath" ).toElement().text();
if ( !root.namedItem( "articleSavePath" ).isNull() )
c.articleSavePath = root.namedItem( "articleSavePath" ).toElement().text();
2012-11-28 19:32:37 +00:00
if ( !root.namedItem( "editDictionaryCommandLine" ).isNull() )
c.editDictionaryCommandLine = root.namedItem( "editDictionaryCommandLine" ).toElement().text();
2012-12-07 11:59:29 +00:00
if ( !root.namedItem( "maxPictureWidth" ).isNull() )
c.maxPictureWidth = root.namedItem( "maxPictureWidth" ).toElement().text().toInt();
if ( !root.namedItem( "maxHeadwordSize" ).isNull() )
{
unsigned int value = root.namedItem( "maxHeadwordSize" ).toElement().text().toUInt();
if ( value != 0 ) // 0 is invalid value for our purposes
{
c.maxHeadwordSize = value;
}
}
if ( !root.namedItem( "maxHeadwordsToExpand" ).isNull() )
c.maxHeadwordsToExpand = root.namedItem( "maxHeadwordsToExpand" ).toElement().text().toUInt();
QDomNode headwordsDialog = root.namedItem( "headwordsDialog" );
if ( !headwordsDialog.isNull() )
{
if ( !headwordsDialog.namedItem( "searchMode" ).isNull() )
2014-04-16 16:18:28 +00:00
c.headwordsDialog.searchMode = headwordsDialog.namedItem( "searchMode" ).toElement().text().toInt();
if ( !headwordsDialog.namedItem( "matchCase" ).isNull() )
c.headwordsDialog.matchCase = ( headwordsDialog.namedItem( "matchCase" ).toElement().text() == "1" );
if ( !headwordsDialog.namedItem( "autoApply" ).isNull() )
c.headwordsDialog.autoApply = ( headwordsDialog.namedItem( "autoApply" ).toElement().text() == "1" );
if ( !headwordsDialog.namedItem( "headwordsExportPath" ).isNull() )
c.headwordsDialog.headwordsExportPath = headwordsDialog.namedItem( "headwordsExportPath" ).toElement().text();
if ( !headwordsDialog.namedItem( "headwordsDialogGeometry" ).isNull() )
c.headwordsDialog.headwordsDialogGeometry = QByteArray::fromBase64( headwordsDialog.namedItem( "headwordsDialogGeometry" ).toElement().text().toLatin1() );
}
return c;
}
namespace {
void saveGroup( Group const & data, QDomElement & group )
{
QDomDocument dd = group.ownerDocument();
QDomAttr id = dd.createAttribute( "id" );
id.setValue( QString::number( data.id ) );
group.setAttributeNode( id );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( data.name );
group.setAttributeNode( name );
2017-05-05 14:39:51 +00:00
if( data.favoritesFolder.size() )
{
QDomAttr folder = dd.createAttribute( "favoritesFolder" );
folder.setValue( data.favoritesFolder );
group.setAttributeNode( folder );
}
if ( data.icon.size() )
{
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( data.icon );
group.setAttributeNode( icon );
}
if ( data.iconData.size() )
{
QDomAttr iconData = dd.createAttribute( "iconData" );
iconData.setValue( QString::fromLatin1( data.iconData.toBase64() ) );
group.setAttributeNode( iconData );
}
if ( !data.shortcut.isEmpty() )
{
QDomAttr shortcut = dd.createAttribute( "shortcut" );
shortcut.setValue( data.shortcut.toString() );
group.setAttributeNode( shortcut );
}
2012-12-10 12:49:45 +00:00
for( QVector< DictionaryRef >::const_iterator j = data.dictionaries.begin(); j != data.dictionaries.end(); ++j )
{
QDomElement dictionary = dd.createElement( "dictionary" );
group.appendChild( dictionary );
QDomText value = dd.createTextNode( j->id );
dictionary.appendChild( value );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( j->name );
dictionary.setAttributeNode( name );
}
QDomElement muted = dd.createElement( "mutedDictionaries" );
group.appendChild( muted );
for( MutedDictionaries::const_iterator i = data.mutedDictionaries.begin();
i != data.mutedDictionaries.end(); ++i )
{
QDomElement dict = dd.createElement( "mutedDictionary" );
muted.appendChild( dict );
QDomText value = dd.createTextNode( *i );
dict.appendChild( value );
}
for( MutedDictionaries::const_iterator i = data.popupMutedDictionaries.begin();
i != data.popupMutedDictionaries.end(); ++i )
{
QDomElement dict = dd.createElement( "popupMutedDictionary" );
muted.appendChild( dict );
QDomText value = dd.createTextNode( *i );
dict.appendChild( value );
}
}
}
2022-01-09 08:35:07 +00:00
void save( Class const & c )
{
QFile configFile( getConfigFileName() + ".tmp" );
if ( !configFile.open( QFile::WriteOnly ) )
throw exCantWriteConfigFile();
QDomDocument dd;
QDomElement root = dd.createElement( "config" );
dd.appendChild( root );
{
QDomElement paths = dd.createElement( "paths" );
root.appendChild( paths );
for( Paths::const_iterator i = c.paths.begin(); i != c.paths.end(); ++i )
{
QDomElement path = dd.createElement( "path" );
paths.appendChild( path );
QDomAttr recursive = dd.createAttribute( "recursive" );
recursive.setValue( i->recursive ? "1" : "0" );
path.setAttributeNode( recursive );
QDomText value = dd.createTextNode( i->path );
path.appendChild( value );
}
}
{
QDomElement soundDirs = dd.createElement( "sounddirs" );
root.appendChild( soundDirs );
for( SoundDirs::const_iterator i = c.soundDirs.begin(); i != c.soundDirs.end(); ++i )
{
QDomElement soundDir = dd.createElement( "sounddir" );
soundDirs.appendChild( soundDir );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( i->name );
soundDir.setAttributeNode( name );
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( i->iconFilename );
soundDir.setAttributeNode( icon );
QDomText value = dd.createTextNode( i->path );
soundDir.appendChild( value );
}
}
{
QDomElement dictionaryOrder = dd.createElement( "dictionaryOrder" );
root.appendChild( dictionaryOrder );
saveGroup( c.dictionaryOrder, dictionaryOrder );
}
{
QDomElement inactiveDictionaries = dd.createElement( "inactiveDictionaries" );
root.appendChild( inactiveDictionaries );
saveGroup( c.inactiveDictionaries, inactiveDictionaries );
}
{
QDomElement groups = dd.createElement( "groups" );
root.appendChild( groups );
QDomAttr nextId = dd.createAttribute( "nextId" );
nextId.setValue( QString::number( c.groups.nextId ) );
groups.setAttributeNode( nextId );
for( Groups::const_iterator i = c.groups.begin(); i != c.groups.end(); ++i )
{
QDomElement group = dd.createElement( "group" );
groups.appendChild( group );
saveGroup( *i, group );
}
}
2009-04-09 14:15:01 +00:00
{
QDomElement hunspell = dd.createElement( "hunspell" );
QDomAttr path = dd.createAttribute( "dictionariesPath" );
path.setValue( c.hunspell.dictionariesPath );
hunspell.setAttributeNode( path );
root.appendChild( hunspell );
2012-12-10 12:49:45 +00:00
for( int x = 0; x < c.hunspell.enabledDictionaries.size(); ++x )
2009-04-09 14:15:01 +00:00
{
QDomElement en = dd.createElement( "enabled" );
2012-12-10 12:49:45 +00:00
QDomText value = dd.createTextNode( c.hunspell.enabledDictionaries.at( x ) );
2009-04-09 14:15:01 +00:00
en.appendChild( value );
hunspell.appendChild( en );
}
}
{
2009-05-11 23:37:18 +00:00
// Russian translit
QDomElement transliteration = dd.createElement( "transliteration" );
root.appendChild( transliteration );
QDomElement opt = dd.createElement( "enableRussianTransliteration" );
opt.appendChild( dd.createTextNode( c.transliteration.enableRussianTransliteration ? "1":"0" ) );
transliteration.appendChild( opt );
2009-05-11 23:37:18 +00:00
// German translit
opt = dd.createElement( "enableGermanTransliteration" );
opt.appendChild( dd.createTextNode( c.transliteration.enableGermanTransliteration ? "1":"0" ) );
transliteration.appendChild( opt );
2009-05-11 23:37:18 +00:00
// Greek translit
opt = dd.createElement( "enableGreekTransliteration" );
opt.appendChild( dd.createTextNode( c.transliteration.enableGreekTransliteration ? "1":"0" ) );
transliteration.appendChild( opt );
2013-01-18 19:21:56 +00:00
// Belarusian translit
opt = dd.createElement( "enableBelarusianTransliteration" );
opt.appendChild( dd.createTextNode( c.transliteration.enableBelarusianTransliteration ? "1":"0" ) );
transliteration.appendChild( opt );
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
// Chinese
QDomElement chinese = dd.createElement( "chinese" );
transliteration.appendChild( chinese );
opt = dd.createElement( "enable" );
opt.appendChild( dd.createTextNode( c.transliteration.chinese.enable ? "1":"0" ) );
chinese.appendChild( opt );
opt = dd.createElement( "enableSCToTWConversion" );
opt.appendChild( dd.createTextNode( c.transliteration.chinese.enableSCToTWConversion ? "1":"0" ) );
chinese.appendChild( opt );
opt = dd.createElement( "enableSCToHKConversion" );
opt.appendChild( dd.createTextNode( c.transliteration.chinese.enableSCToHKConversion ? "1":"0" ) );
chinese.appendChild( opt );
opt = dd.createElement( "enableTCToSCConversion" );
opt.appendChild( dd.createTextNode( c.transliteration.chinese.enableTCToSCConversion ? "1":"0" ) );
chinese.appendChild( opt );
#endif
2009-05-11 23:37:18 +00:00
// Romaji
QDomElement romaji = dd.createElement( "romaji" );
transliteration.appendChild( romaji );
opt = dd.createElement( "enable" );
opt.appendChild( dd.createTextNode( c.transliteration.romaji.enable ? "1":"0" ) );
romaji.appendChild( opt );
2009-05-11 23:37:18 +00:00
opt = dd.createElement( "enableHepburn" );
opt.appendChild( dd.createTextNode( c.transliteration.romaji.enableHepburn ? "1":"0" ) );
romaji.appendChild( opt );
2009-05-11 23:37:18 +00:00
opt = dd.createElement( "enableNihonShiki" );
opt.appendChild( dd.createTextNode( c.transliteration.romaji.enableNihonShiki ? "1":"0" ) );
romaji.appendChild( opt );
2009-05-11 23:37:18 +00:00
opt = dd.createElement( "enableKunreiShiki" );
2009-05-11 23:37:18 +00:00
opt.appendChild( dd.createTextNode( c.transliteration.romaji.enableKunreiShiki ? "1":"0" ) );
romaji.appendChild( opt );
2009-05-11 23:37:18 +00:00
opt = dd.createElement( "enableHiragana" );
opt.appendChild( dd.createTextNode( c.transliteration.romaji.enableHiragana ? "1":"0" ) );
romaji.appendChild( opt );
2009-05-11 23:37:18 +00:00
opt = dd.createElement( "enableKatakana" );
opt.appendChild( dd.createTextNode( c.transliteration.romaji.enableKatakana ? "1":"0" ) );
romaji.appendChild( opt );
}
{
// Forvo
QDomElement forvo = dd.createElement( "forvo" );
root.appendChild( forvo );
QDomElement opt = dd.createElement( "enable" );
opt.appendChild( dd.createTextNode( c.forvo.enable ? "1":"0" ) );
forvo.appendChild( opt );
opt = dd.createElement( "apiKey" );
opt.appendChild( dd.createTextNode( c.forvo.apiKey ) );
forvo.appendChild( opt );
opt = dd.createElement( "languageCodes" );
opt.appendChild( dd.createTextNode( c.forvo.languageCodes ) );
forvo.appendChild( opt );
}
2009-05-11 23:37:18 +00:00
{
QDomElement mws = dd.createElement( "mediawikis" );
root.appendChild( mws );
for( MediaWikis::const_iterator i = c.mediawikis.begin(); i != c.mediawikis.end(); ++i )
{
QDomElement mw = dd.createElement( "mediawiki" );
mws.appendChild( mw );
QDomAttr id = dd.createAttribute( "id" );
id.setValue( i->id );
mw.setAttributeNode( id );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( i->name );
mw.setAttributeNode( name );
QDomAttr url = dd.createAttribute( "url" );
url.setValue( i->url );
mw.setAttributeNode( url );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( i->enabled ? "1" : "0" );
mw.setAttributeNode( enabled );
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( i->icon );
mw.setAttributeNode( icon );
}
}
{
QDomElement wss = dd.createElement( "websites" );
root.appendChild( wss );
for( WebSites::const_iterator i = c.webSites.begin(); i != c.webSites.end(); ++i )
{
QDomElement ws = dd.createElement( "website" );
wss.appendChild( ws );
QDomAttr id = dd.createAttribute( "id" );
id.setValue( i->id );
ws.setAttributeNode( id );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( i->name );
ws.setAttributeNode( name );
QDomAttr url = dd.createAttribute( "url" );
url.setValue( i->url );
ws.setAttributeNode( url );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( i->enabled ? "1" : "0" );
ws.setAttributeNode( enabled );
2012-12-03 12:48:31 +00:00
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( i->iconFilename );
ws.setAttributeNode( icon );
QDomAttr inside_iframe = dd.createAttribute( "inside_iframe" );
inside_iframe.setValue( i->inside_iframe ? "1" : "0" );
ws.setAttributeNode( inside_iframe );
}
}
2014-04-30 12:55:53 +00:00
{
QDomElement dss = dd.createElement( "dictservers" );
root.appendChild( dss );
for( DictServers::const_iterator i = c.dictServers.begin(); i != c.dictServers.end(); ++i )
{
QDomElement ds = dd.createElement( "server" );
dss.appendChild( ds );
QDomAttr id = dd.createAttribute( "id" );
id.setValue( i->id );
ds.setAttributeNode( id );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( i->name );
ds.setAttributeNode( name );
QDomAttr url = dd.createAttribute( "url" );
url.setValue( i->url );
ds.setAttributeNode( url );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( i->enabled ? "1" : "0" );
ds.setAttributeNode( enabled );
QDomAttr databases = dd.createAttribute( "databases" );
databases.setValue( i->databases );
ds.setAttributeNode( databases );
QDomAttr strategies = dd.createAttribute( "strategies" );
strategies.setValue( i->strategies );
ds.setAttributeNode( strategies );
2014-04-30 12:55:53 +00:00
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( i->iconFilename );
ds.setAttributeNode( icon );
}
}
{
QDomElement programs = dd.createElement( "programs" );
root.appendChild( programs );
for( Programs::const_iterator i = c.programs.begin(); i != c.programs.end(); ++i )
{
QDomElement p = dd.createElement( "program" );
programs.appendChild( p );
QDomAttr id = dd.createAttribute( "id" );
id.setValue( i->id );
p.setAttributeNode( id );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( i->name );
p.setAttributeNode( name );
QDomAttr commandLine = dd.createAttribute( "commandLine" );
commandLine.setValue( i->commandLine );
p.setAttributeNode( commandLine );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( i->enabled ? "1" : "0" );
p.setAttributeNode( enabled );
QDomAttr type = dd.createAttribute( "type" );
type.setValue( QString::number( i->type ) );
p.setAttributeNode( type );
2012-12-07 11:59:59 +00:00
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( i->iconFilename );
p.setAttributeNode( icon );
}
}
{
QDomNode ves = dd.createElement( "voiceEngines" );
root.appendChild( ves );
for ( VoiceEngines::const_iterator i = c.voiceEngines.begin(); i != c.voiceEngines.end(); ++i )
{
QDomElement v = dd.createElement( "voiceEngine" );
ves.appendChild( v );
QDomAttr id = dd.createAttribute( "id" );
id.setValue( i->id );
v.setAttributeNode( id );
QDomAttr name = dd.createAttribute( "name" );
name.setValue( i->name );
v.setAttributeNode( name );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( i->enabled ? "1" : "0" );
v.setAttributeNode( enabled );
QDomAttr icon = dd.createAttribute( "icon" );
icon.setValue( i->iconFilename );
v.setAttributeNode( icon );
QDomAttr volume = dd.createAttribute( "volume" );
volume.setValue( QString::number( i->volume ) );
v.setAttributeNode( volume );
QDomAttr rate = dd.createAttribute( "rate" );
rate.setValue( QString::number( i->rate ) );
v.setAttributeNode( rate );
}
}
{
QDomElement muted = dd.createElement( "mutedDictionaries" );
root.appendChild( muted );
saveMutedDictionaries( dd, muted, c.mutedDictionaries );
}
{
QDomElement muted = dd.createElement( "popupMutedDictionaries" );
root.appendChild( muted );
saveMutedDictionaries( dd, muted, c.popupMutedDictionaries );
}
{
QDomElement preferences = dd.createElement( "preferences" );
root.appendChild( preferences );
QDomElement opt = dd.createElement( "interfaceLanguage" );
opt.appendChild( dd.createTextNode( c.preferences.interfaceLanguage ) );
preferences.appendChild( opt );
opt = dd.createElement( "webFontFamily" );
opt.appendChild( dd.createTextNode( c.preferences.webFontFamily ) );
preferences.appendChild( opt );
2014-06-25 14:01:11 +00:00
opt = dd.createElement( "helpLanguage" );
opt.appendChild( dd.createTextNode( c.preferences.helpLanguage ) );
preferences.appendChild( opt );
opt = dd.createElement( "displayStyle" );
opt.appendChild( dd.createTextNode( c.preferences.displayStyle ) );
preferences.appendChild( opt );
opt = dd.createElement( "newTabsOpenAfterCurrentOne" );
opt.appendChild( dd.createTextNode( c.preferences.newTabsOpenAfterCurrentOne ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "newTabsOpenInBackground" );
opt.appendChild( dd.createTextNode( c.preferences.newTabsOpenInBackground ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "hideSingleTab" );
opt.appendChild( dd.createTextNode( c.preferences.hideSingleTab ? "1":"0" ) );
preferences.appendChild( opt );
2011-11-02 23:37:50 +00:00
opt = dd.createElement( "mruTabOrder" );
opt.appendChild( dd.createTextNode( c.preferences.mruTabOrder ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "hideMenubar" );
opt.appendChild( dd.createTextNode( c.preferences.hideMenubar ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "enableTrayIcon" );
opt.appendChild( dd.createTextNode( c.preferences.enableTrayIcon ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "startToTray" );
opt.appendChild( dd.createTextNode( c.preferences.startToTray ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "closeToTray" );
opt.appendChild( dd.createTextNode( c.preferences.closeToTray ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "autoStart" );
opt.appendChild( dd.createTextNode( c.preferences.autoStart ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "doubleClickTranslates" );
opt.appendChild( dd.createTextNode( c.preferences.doubleClickTranslates ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "selectWordBySingleClick" );
opt.appendChild( dd.createTextNode( c.preferences.selectWordBySingleClick ? "1":"0" ) );
preferences.appendChild( opt );
Add "Automatically scroll to target article" option When a user clicks on a link in a dictionary or requests translation of a word by double-clicking or translates selection via the context menu, at first the article from the highest-priority dictionary is at the top. Then, after approximately one second, the article from the dictionary, out of which the translation was requested, becomes current and the view scrolls down to this article placing it on top, hiding articles from the dictionaries above it. Such application behavior is inconvenient in some workflows so that the user manually navigates to the top dictionary translation when this automatic scrolling happens. For example: a user has English->Russian dictionaries and English->English dictionaries. The English->Russian dictionaries are higher up in the dictionary order because they provide easier/faster to understand translations. Some rare words and phrases are missing from the English->Russian dictionaries however. Thus the user occasionally reads the English explanation of a word/phrase. When the user double-clicks on a word or follows a link in the English->English dictionary article, she would rather see translations from the preferable English->Russian dictionaries. The new option allows to disable automatic scrolling and ensure that articles from higher-priority dictionaries are visible. The option doesn't affect backward/forward navigation via arrow buttons or Alt+Arrow shortcuts: these still scroll to the stored vertical position among articles. This remaining automatic scrolling happens much faster, is not a problem for the described use case and hopefully for other use cases.
2019-01-11 13:50:40 +00:00
opt = dd.createElement( "autoScrollToTargetArticle" );
opt.appendChild( dd.createTextNode( c.preferences.autoScrollToTargetArticle ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "escKeyHidesMainWindow" );
opt.appendChild( dd.createTextNode( c.preferences.escKeyHidesMainWindow ? "1":"0" ) );
preferences.appendChild( opt );
2009-04-30 19:57:25 +00:00
opt = dd.createElement( "zoomFactor" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.zoomFactor ) ) );
preferences.appendChild( opt );
2014-06-23 15:11:15 +00:00
opt = dd.createElement( "helpZoomFactor" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.helpZoomFactor ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "wordsZoomLevel" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.wordsZoomLevel ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "enableMainWindowHotkey" );
opt.appendChild( dd.createTextNode( c.preferences.enableMainWindowHotkey ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "mainWindowHotkey" );
opt.appendChild( dd.createTextNode( c.preferences.mainWindowHotkey.toKeySequence().toString() ) );
preferences.appendChild( opt );
opt = dd.createElement( "enableClipboardHotkey" );
opt.appendChild( dd.createTextNode( c.preferences.enableClipboardHotkey ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "clipboardHotkey" );
opt.appendChild( dd.createTextNode( c.preferences.clipboardHotkey.toKeySequence().toString() ) );
preferences.appendChild( opt );
opt = dd.createElement( "enableScanPopup" );
opt.appendChild( dd.createTextNode( c.preferences.enableScanPopup ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "startWithScanPopupOn" );
opt.appendChild( dd.createTextNode( c.preferences.startWithScanPopupOn ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "enableScanPopupModifiers" );
opt.appendChild( dd.createTextNode( c.preferences.enableScanPopupModifiers ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "scanPopupModifiers" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.scanPopupModifiers ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "scanPopupAltMode" );
opt.appendChild( dd.createTextNode( c.preferences.scanPopupAltMode ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "scanPopupAltModeSecs" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.scanPopupAltModeSecs ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "ignoreOwnClipboardChanges" );
opt.appendChild( dd.createTextNode( c.preferences.ignoreOwnClipboardChanges ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "scanToMainWindow" );
opt.appendChild( dd.createTextNode( c.preferences.scanToMainWindow ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "ignoreDiacritics" );
opt.appendChild( dd.createTextNode( c.preferences.ignoreDiacritics ? "1":"0" ) );
preferences.appendChild( opt );
2022-04-23 08:15:23 +00:00
opt = dd.createElement( "ignorePunctuation" );
opt.appendChild( dd.createTextNode( c.preferences.ignorePunctuation ? "1":"0" ) );
preferences.appendChild( opt );
#ifdef HAVE_X11
opt = dd.createElement( "showScanFlag" );
opt.appendChild( dd.createTextNode( c.preferences.showScanFlag? "1":"0" ) );
preferences.appendChild( opt );
#endif
Allow customizing unpinned scan popup window flags on X11 with Qt5 My tests in many desktop environments and window managers indicate that no single configuration works perfectly in all environments. There are also behavior differences between Qt::Popup and Qt::Tool flags, which are not exactly bugs, so I suppose users might subjectively prefer different options. Customizing the flags allows the user to prevent unpinned scan popup window flickering with Qt5 on Linux. In a way adding these options fixes issue #645, which is: the scan popup window blinks rapidly, barely noticeably in some applications, such as Calibre ebook-viewer and Chromium. In this case the scan popup window usually ends up hidden when selection ends, unless it was finished with a jerk. I have tested the new options in 9 desktop environments and window managers: at least one configuration for each eliminates #645 and makes the scan popup window work the same as with Qt4 in this regard: the popup window remains visible, text in the popup's translation line keeps up with the text selection in the external application, and the selected text is being translated on the fly. Moreover, for each tested DE/WM, at least one configuration makes the scan popup window work perfectly as far as I am concerned. This issue was partially worked around with a 200ms scan popup delay timer in the recent commit 58e41fe3ceb769cab37608e36637044e597ba1f8 for the duplicate issue #854. However the timer solution is incomplete because it requires the user to select text quickly and without delays. If global mouse selection does not change for 200ms while the left mouse button is held down, the user will likely not see the scan popup when (s)he finishes selection, and will have to try selecting again - hopefully faster this time. The 200ms delay is no longer critically important after this commit, but it is still beneficial: the lookup query changes less often, which in turn reduces article definition update frequency. So the delay improves the UI (perhaps subjectively) and performance.
2018-04-09 17:50:23 +00:00
opt = dd.createElement( "scanPopupUnpinnedWindowFlags" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.scanPopupUnpinnedWindowFlags ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "scanPopupUnpinnedBypassWMHint" );
opt.appendChild( dd.createTextNode( c.preferences.scanPopupUnpinnedBypassWMHint ? "1":"0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "pronounceOnLoadMain" );
opt.appendChild( dd.createTextNode( c.preferences.pronounceOnLoadMain ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "pronounceOnLoadPopup" );
opt.appendChild( dd.createTextNode( c.preferences.pronounceOnLoadPopup ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "useInternalPlayer" );
opt.appendChild( dd.createTextNode( c.preferences.useInternalPlayer ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "internalPlayerBackend" );
opt.appendChild( dd.createTextNode( c.preferences.internalPlayerBackend.uiName() ) );
preferences.appendChild( opt );
opt = dd.createElement( "audioPlaybackProgram" );
opt.appendChild( dd.createTextNode( c.preferences.audioPlaybackProgram ) );
preferences.appendChild( opt );
opt = dd.createElement( "alwaysOnTop" );
opt.appendChild( dd.createTextNode( c.preferences.alwaysOnTop ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "searchInDock" );
opt.appendChild( dd.createTextNode( c.preferences.searchInDock ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "historyStoreInterval" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.historyStoreInterval ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "favoritesStoreInterval" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.favoritesStoreInterval ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "confirmFavoritesDeletion" );
opt.appendChild( dd.createTextNode( c.preferences.confirmFavoritesDeletion ? "1" : "0" ) );
preferences.appendChild( opt );
{
QDomElement proxy = dd.createElement( "proxyserver" );
preferences.appendChild( proxy );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( c.preferences.proxyServer.enabled ? "1" : "0" );
proxy.setAttributeNode( enabled );
2014-04-01 17:00:13 +00:00
QDomAttr useSystemProxy = dd.createAttribute( "useSystemProxy" );
useSystemProxy.setValue( c.preferences.proxyServer.useSystemProxy ? "1" : "0" );
proxy.setAttributeNode( useSystemProxy );
opt = dd.createElement( "type" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.proxyServer.type ) ) );
proxy.appendChild( opt );
opt = dd.createElement( "host" );
opt.appendChild( dd.createTextNode( c.preferences.proxyServer.host ) );
proxy.appendChild( opt );
opt = dd.createElement( "port" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.proxyServer.port ) ) );
proxy.appendChild( opt );
opt = dd.createElement( "user" );
opt.appendChild( dd.createTextNode( c.preferences.proxyServer.user ) );
proxy.appendChild( opt );
opt = dd.createElement( "password" );
opt.appendChild( dd.createTextNode( c.preferences.proxyServer.password ) );
proxy.appendChild( opt );
2014-04-03 14:21:02 +00:00
opt = dd.createElement( "systemProxyUser" );
opt.appendChild( dd.createTextNode( c.preferences.proxyServer.systemProxyUser ) );
proxy.appendChild( opt );
opt = dd.createElement( "systemProxyPassword" );
opt.appendChild( dd.createTextNode( c.preferences.proxyServer.systemProxyPassword ) );
proxy.appendChild( opt );
}
//anki connect
{
QDomElement proxy = dd.createElement( "ankiConnectServer" );
preferences.appendChild( proxy );
QDomAttr enabled = dd.createAttribute( "enabled" );
enabled.setValue( c.preferences.ankiConnectServer.enabled ? "1" : "0" );
proxy.setAttributeNode( enabled );
opt = dd.createElement( "host" );
opt.appendChild( dd.createTextNode( c.preferences.ankiConnectServer.host ) );
proxy.appendChild( opt );
opt = dd.createElement( "port" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.ankiConnectServer.port ) ) );
proxy.appendChild( opt );
opt = dd.createElement( "deck" );
opt.appendChild( dd.createTextNode( c.preferences.ankiConnectServer.deck ) );
proxy.appendChild( opt );
opt = dd.createElement( "model" );
opt.appendChild( dd.createTextNode( c.preferences.ankiConnectServer.model ) );
proxy.appendChild( opt );
}
opt = dd.createElement( "checkForNewReleases" );
opt.appendChild( dd.createTextNode( c.preferences.checkForNewReleases ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "disallowContentFromOtherSites" );
opt.appendChild( dd.createTextNode( c.preferences.disallowContentFromOtherSites ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "enableWebPlugins" );
opt.appendChild( dd.createTextNode( c.preferences.enableWebPlugins ? "1" : "0" ) );
preferences.appendChild( opt );
2012-02-16 14:56:25 +00:00
opt = dd.createElement( "hideGoldenDictHeader" );
opt.appendChild( dd.createTextNode( c.preferences.hideGoldenDictHeader ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "maxNetworkCacheSize" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.maxNetworkCacheSize ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "clearNetworkCacheOnExit" );
opt.appendChild( dd.createTextNode( c.preferences.clearNetworkCacheOnExit ? "1" : "0" ) );
preferences.appendChild( opt );
2012-02-16 14:56:25 +00:00
opt = dd.createElement( "maxStringsInHistory" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.maxStringsInHistory ) ) );
preferences.appendChild( opt );
2012-09-10 18:00:29 +00:00
opt = dd.createElement( "storeHistory" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.storeHistory ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "alwaysExpandOptionalParts" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.alwaysExpandOptionalParts ) ) );
preferences.appendChild( opt );
2012-12-10 14:14:13 +00:00
opt = dd.createElement( "addonStyle" );
opt.appendChild( dd.createTextNode( c.preferences.addonStyle ) );
preferences.appendChild( opt );
opt = dd.createElement( "collapseBigArticles" );
opt.appendChild( dd.createTextNode( c.preferences.collapseBigArticles ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "articleSizeLimit" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.articleSizeLimit ) ) );
preferences.appendChild( opt );
Add options to limit input phrase length When a long text is accidentally selected or copied while the scan popup is on, Goldendict spends a lot of CPU time to gradually create a "No translation..." article. When translation of huge (e.g. 15 MB) text from the clipboard is (accidentally) requested, Goldendict freezes for a while. Turning the added input phrase limit option on eliminates this waste of the CPU time. I have implemented these options primarily for selection and clipboard, but they also affect mouse-over translation on Windows and command line translation requests. This is mostly because I did not bother to limit the options' scope. I guess hovering over an extremely long text without spaces (e.g. Base64-encoded) could cause the same performance issue on Windows. The command-line translation could be requested from a script that integrates Goldendict with some other application, from which long text could be sent for translation by accident. I hope that the default value of 200 characters will be sufficient for just about any real-world user input in any language. The option is on by default, because the default length limit is generous and any longer text is unlikely to be sent for translation intentionally. My personal preference for the input phrase length limit is 100 symbols. ArticleView::pasteTriggered() didn't call QString::simplified() on the text retrieved from the clipboard. I assumed this was an oversight, so now it *is* called - indirectly, via Preferences::sanitizeInputPhrase().
2020-11-06 19:51:30 +00:00
opt = dd.createElement( "limitInputPhraseLength" );
opt.appendChild( dd.createTextNode( c.preferences.limitInputPhraseLength ? "1" : "0" ) );
preferences.appendChild( opt );
opt = dd.createElement( "inputPhraseLengthLimit" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.inputPhraseLengthLimit ) ) );
preferences.appendChild( opt );
opt = dd.createElement( "maxDictionaryRefsInContextMenu" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.maxDictionaryRefsInContextMenu ) ) );
preferences.appendChild( opt );
#ifndef Q_WS_X11
opt = dd.createElement( "trackClipboardChanges" );
opt.appendChild( dd.createTextNode( c.preferences.trackClipboardChanges ? "1" : "0" ) );
preferences.appendChild( opt );
#endif
opt = dd.createElement( "synonymSearchEnabled" );
opt.appendChild( dd.createTextNode( c.preferences.synonymSearchEnabled ? "1" : "0" ) );
preferences.appendChild( opt );
{
QDomNode hd = dd.createElement( "fullTextSearch" );
preferences.appendChild( hd );
QDomElement opt = dd.createElement( "searchMode" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.fts.searchMode ) ) );
hd.appendChild( opt );
opt = dd.createElement( "matchCase" );
opt.appendChild( dd.createTextNode( c.preferences.fts.matchCase ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "maxArticlesPerDictionary" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.fts.maxArticlesPerDictionary ) ) );
hd.appendChild( opt );
opt = dd.createElement( "maxDistanceBetweenWords" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.fts.maxDistanceBetweenWords ) ) );
hd.appendChild( opt );
opt = dd.createElement( "useMaxArticlesPerDictionary" );
opt.appendChild( dd.createTextNode( c.preferences.fts.useMaxArticlesPerDictionary ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "useMaxDistanceBetweenWords" );
opt.appendChild( dd.createTextNode( c.preferences.fts.useMaxDistanceBetweenWords ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "dialogGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.preferences.fts.dialogGeometry.toBase64() ) ) );
hd.appendChild( opt );
opt = dd.createElement( "disabledTypes" );
opt.appendChild( dd.createTextNode( c.preferences.fts.disabledTypes ) );
hd.appendChild( opt );
opt = dd.createElement( "enabled" );
opt.appendChild( dd.createTextNode( c.preferences.fts.enabled ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "ignoreWordsOrder" );
opt.appendChild( dd.createTextNode( c.preferences.fts.ignoreWordsOrder ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "ignoreDiacritics" );
opt.appendChild( dd.createTextNode( c.preferences.fts.ignoreDiacritics ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "maxDictionarySize" );
opt.appendChild( dd.createTextNode( QString::number( c.preferences.fts.maxDictionarySize ) ) );
hd.appendChild( opt );
}
}
{
QDomElement opt = dd.createElement( "lastMainGroupId" );
opt.appendChild( dd.createTextNode( QString::number( c.lastMainGroupId ) ) );
root.appendChild( opt );
opt = dd.createElement( "lastPopupGroupId" );
opt.appendChild( dd.createTextNode( QString::number( c.lastPopupGroupId ) ) );
root.appendChild( opt );
opt = dd.createElement( "popupWindowState" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.popupWindowState.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "popupWindowGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.popupWindowGeometry.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "pinPopupWindow" );
opt.appendChild( dd.createTextNode( c.pinPopupWindow ? "1" : "0" ) );
root.appendChild( opt );
opt = dd.createElement( "popupWindowAlwaysOnTop" );
opt.appendChild( dd.createTextNode( c.popupWindowAlwaysOnTop ? "1" : "0" ) );
root.appendChild( opt );
opt = dd.createElement( "mainWindowState" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.mainWindowState.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "mainWindowGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.mainWindowGeometry.toBase64() ) ) );
root.appendChild( opt );
2014-06-23 15:11:15 +00:00
opt = dd.createElement( "helpWindowGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.helpWindowGeometry.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "helpSplitterState" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.helpSplitterState.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "dictInfoGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.dictInfoGeometry.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "inspectorGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.inspectorGeometry.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "dictionariesDialogGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.dictionariesDialogGeometry.toBase64() ) ) );
root.appendChild( opt );
opt = dd.createElement( "timeForNewReleaseCheck" );
opt.appendChild( dd.createTextNode( c.timeForNewReleaseCheck.toString( Qt::ISODate ) ) );
root.appendChild( opt );
opt = dd.createElement( "skippedRelease" );
opt.appendChild( dd.createTextNode( c.skippedRelease ) );
root.appendChild( opt );
opt = dd.createElement( "showingDictBarNames" );
opt.appendChild( dd.createTextNode( c.showingDictBarNames ? "1" : "0" ) );
root.appendChild( opt );
opt = dd.createElement( "usingSmallIconsInToolbars" );
opt.appendChild( dd.createTextNode( c.usingSmallIconsInToolbars ? "1" : "0" ) );
root.appendChild( opt );
if( !c.historyExportPath.isEmpty() )
{
opt = dd.createElement( "historyExportPath" );
opt.appendChild( dd.createTextNode( c.historyExportPath ) );
root.appendChild( opt );
}
2012-11-28 19:32:37 +00:00
if( !c.resourceSavePath.isEmpty() )
{
opt = dd.createElement( "resourceSavePath" );
opt.appendChild( dd.createTextNode( c.resourceSavePath ) );
root.appendChild( opt );
}
if( !c.articleSavePath.isEmpty() )
{
opt = dd.createElement( "articleSavePath" );
opt.appendChild( dd.createTextNode( c.articleSavePath ) );
root.appendChild( opt );
}
2012-11-28 19:32:37 +00:00
opt = dd.createElement( "editDictionaryCommandLine" );
opt.appendChild( dd.createTextNode( c.editDictionaryCommandLine ) );
root.appendChild( opt );
2012-12-07 11:59:29 +00:00
opt = dd.createElement( "maxPictureWidth" );
opt.appendChild( dd.createTextNode( QString::number( c.maxPictureWidth ) ) );
root.appendChild( opt );
opt = dd.createElement( "maxHeadwordSize" );
opt.appendChild( dd.createTextNode( QString::number( c.maxHeadwordSize ) ) );
root.appendChild( opt );
opt = dd.createElement( "maxHeadwordsToExpand" );
opt.appendChild( dd.createTextNode( QString::number( c.maxHeadwordsToExpand ) ) );
root.appendChild( opt );
}
{
QDomNode hd = dd.createElement( "headwordsDialog" );
root.appendChild( hd );
QDomElement opt = dd.createElement( "searchMode" );
opt.appendChild( dd.createTextNode( QString::number( c.headwordsDialog.searchMode ) ) );
hd.appendChild( opt );
opt = dd.createElement( "matchCase" );
opt.appendChild( dd.createTextNode( c.headwordsDialog.matchCase ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "autoApply" );
opt.appendChild( dd.createTextNode( c.headwordsDialog.autoApply ? "1" : "0" ) );
hd.appendChild( opt );
opt = dd.createElement( "headwordsExportPath" );
opt.appendChild( dd.createTextNode( c.headwordsDialog.headwordsExportPath ) );
hd.appendChild( opt );
opt = dd.createElement( "headwordsDialogGeometry" );
opt.appendChild( dd.createTextNode( QString::fromLatin1( c.headwordsDialog.headwordsDialogGeometry.toBase64() ) ) );
hd.appendChild( opt );
}
QByteArray result( dd.toByteArray() );
if ( configFile.write( result ) != result.size() )
throw exCantWriteConfigFile();
configFile.close();
renameAtomically( configFile.fileName(), getConfigFileName() );
}
QString getConfigFileName()
{
return getHomeDir().absoluteFilePath( "config" );
}
2022-01-09 08:35:07 +00:00
QString getConfigDir()
{
return getHomeDir().path() + QDir::separator();
}
2022-01-09 08:35:07 +00:00
QString getIndexDir()
{
QDir result = getHomeDir();
#ifdef XDG_BASE_DIRECTORY_COMPLIANCE
// store index in XDG_CACHE_HOME in non-portable version
// *and* when an old index directory in GoldenDict home doesn't exist
if ( !isPortableVersion() && !result.exists( "index" ) )
{
result.setPath( getCacheDir() );
}
#endif
result.mkpath( "index" );
if ( !result.cd( "index" ) )
throw exCantUseIndexDir();
return result.path() + QDir::separator();
}
2022-01-09 08:35:07 +00:00
QString getPidFileName()
{
return getHomeDir().filePath( "pid" );
}
2022-01-09 08:35:07 +00:00
QString getHistoryFileName()
{
QString homeHistoryPath = getHomeDir().filePath( "history" );
#ifdef XDG_BASE_DIRECTORY_COMPLIANCE
// use separate data dir for history, if it is not already stored alongside
// configuration in non-portable mode
if ( !isPortableVersion() && !QFile::exists( homeHistoryPath ) )
{
return getDataDir().filePath( "history" );
}
#endif
return homeHistoryPath;
}
2022-01-09 08:35:07 +00:00
QString getFavoritiesFileName()
2017-05-05 14:39:51 +00:00
{
return getHomeDir().filePath( "favorites" );
}
2022-01-09 08:35:07 +00:00
QString getUserCssFileName()
{
return getHomeDir().filePath( "article-style.css" );
}
2022-01-09 08:35:07 +00:00
QString getUserCssPrintFileName()
2009-05-01 12:20:33 +00:00
{
return getHomeDir().filePath( "article-style-print.css" );
}
2022-01-09 08:35:07 +00:00
QString getUserQtCssFileName()
{
return getHomeDir().filePath( "qt-style.css" );
}
QString getProgramDataDir() noexcept
{
if ( isPortableVersion() )
return QCoreApplication::applicationDirPath();
#ifdef PROGRAM_DATA_DIR
return PROGRAM_DATA_DIR;
#else
return QCoreApplication::applicationDirPath();
#endif
}
QString getLocDir() noexcept
{
2022-09-21 15:40:06 +00:00
if ( QDir( getProgramDataDir() ).cd( "locale" ) )
return getProgramDataDir() + "/locale";
else
return QCoreApplication::applicationDirPath() + "/locale";
}
QString getHelpDir() noexcept
2014-06-24 13:55:06 +00:00
{
if ( QDir( getProgramDataDir() ).cd( "help" ) )
return getProgramDataDir() + "/help";
else
return QCoreApplication::applicationDirPath() + "/help";
}
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
QString getOpenCCDir() noexcept
{
#if defined( Q_OS_WIN )
if ( QDir( "opencc" ).exists() )
return "opencc";
else
return QCoreApplication::applicationDirPath() + "/opencc";
#elif defined( Q_OS_MAC )
QString path = QCoreApplication::applicationDirPath() + "/opencc";
if ( QDir( path ).exists() )
return path;
return QString();
#else
return QString();
#endif
}
#endif
bool isPortableVersion() noexcept
{
struct IsPortable
{
bool isPortable;
IsPortable(): isPortable( QFileInfo( portableHomeDirPath() ).isDir() )
{}
};
static IsPortable p;
return p.isPortable;
}
QString getPortableVersionDictionaryDir() noexcept
{
if ( isPortableVersion() )
return getProgramDataDir() + "/content";
else
return QString();
}
QString getPortableVersionMorphoDir() noexcept
{
if ( isPortableVersion() )
return getPortableVersionDictionaryDir() + "/morphology";
else
return QString();
}
2022-02-13 06:26:45 +00:00
QString getStylesDir()
2012-12-10 14:14:13 +00:00
{
QDir result = getHomeDir();
result.mkpath( "styles" );
if ( !result.cd( "styles" ) )
return QString();
return result.path() + QDir::separator();
}
QString getCacheDir() noexcept
{
return isPortableVersion() ? portableHomeDirPath() + "/cache"
#ifdef HAVE_X11
: QStandardPaths::writableLocation( QStandardPaths::GenericCacheLocation ) + "/goldendict";
#else
: QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
#endif
}
QString getNetworkCacheDir() noexcept
{
return getCacheDir() + "/network";
}
}