goldendict-ng/src/config.hh
2024-09-01 20:09:22 -04:00

1005 lines
24 KiB
C++

/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef __CONFIG_HH_INCLUDED__
#define __CONFIG_HH_INCLUDED__
#include <QObject>
#include <QList>
#include <QString>
#include <QSize>
#include <QDateTime>
#include <QKeySequence>
#include <QSet>
#include <QMetaType>
#include "ex.hh"
#include <QDomDocument>
#include <QLocale>
#include <optional>
#include <QThread>
/// GoldenDict's configuration
namespace Config {
/// Dictionaries which are temporarily disabled via the dictionary bar.
typedef QSet< QString > MutedDictionaries;
/// A path where to search for the dictionaries
struct Path
{
QString path;
bool recursive;
Path():
recursive( false )
{
}
Path( QString const & path_, bool recursive_ ):
path( path_ ),
recursive( recursive_ )
{
}
bool operator==( Path const & other ) const
{
return path == other.path && recursive == other.recursive;
}
};
/// A list of paths where to search for the dictionaries
typedef QList< Path > Paths;
/// A directory holding bunches of audiofiles, which is indexed into a separate
/// dictionary.
struct SoundDir
{
QString path, name;
QString iconFilename;
SoundDir() {}
SoundDir( QString const & path_, QString const & name_, QString iconFilename_ = "" ):
path( path_ ),
name( name_ ),
iconFilename( iconFilename_ )
{
}
bool operator==( SoundDir const & other ) const
{
return path == other.path && name == other.name && iconFilename == other.iconFilename;
}
};
/// A list of SoundDirs
typedef QList< SoundDir > SoundDirs;
struct DictionaryRef
{
QString id; // Dictionrary id, which is usually an md5 hash
QString name; // Dictionary name, used to recover when its id changes
DictionaryRef() {}
DictionaryRef( QString const & id_, QString const & name_ ):
id( id_ ),
name( name_ )
{
}
bool operator==( DictionaryRef const & other ) const
{
return id == other.id && name == other.name;
}
};
/// A dictionary group
struct Group
{
unsigned id;
QString name, icon;
QByteArray iconData;
QKeySequence shortcut;
QString favoritesFolder;
QList< DictionaryRef > dictionaries;
Config::MutedDictionaries mutedDictionaries; // Disabled via dictionary bar
Config::MutedDictionaries popupMutedDictionaries; // Disabled via dictionary bar in popup
Group():
id( 0 )
{
}
bool operator==( Group const & other ) const
{
return id == other.id && name == other.name && icon == other.icon && favoritesFolder == other.favoritesFolder
&& dictionaries == other.dictionaries && shortcut == other.shortcut
&& mutedDictionaries == other.mutedDictionaries && popupMutedDictionaries == other.popupMutedDictionaries
&& iconData == other.iconData;
}
bool operator!=( Group const & other ) const
{
return !operator==( other );
}
};
/// All the groups
struct Groups: public QList< Group >
{
unsigned nextId; // Id to use to create the group next time
Groups():
nextId( 1 )
{
}
};
/// Proxy server configuration
struct ProxyServer
{
bool enabled;
bool useSystemProxy;
enum Type {
Socks5 = 0,
HttpConnect,
HttpGet
} type;
QString host;
unsigned port;
QString user, password;
QString systemProxyUser, systemProxyPassword;
ProxyServer();
};
struct AnkiConnectServer
{
bool enabled;
QString host;
int port; // Port will be passed to QUrl::setPort() which expects an int.
QString deck;
QString model;
QString word;
QString text;
QString sentence;
AnkiConnectServer();
};
// A hotkey -- currently qt modifiers plus one or two keys
struct HotKey
{
Qt::KeyboardModifiers modifiers;
int key1, key2;
HotKey();
/// Hotkey's constructor, take a QKeySequence's first two keys
/// 1st key's modifier will be the `modifiers` above
/// 1st key without modifier will becomes `key1`
/// 2nd key without modifier will becomes `key2`
/// The relation between the int and qt's KeyCode should consult qt's doc
HotKey( QKeySequence const & );
QKeySequence toKeySequence() const;
};
struct FullTextSearch
{
int searchMode;
bool enabled;
quint32 maxDictionarySize;
quint32 parallelThreads = QThread::idealThreadCount() / 3 + 1;
QByteArray dialogGeometry;
QString disabledTypes;
FullTextSearch():
searchMode( 0 ),
enabled( true ),
maxDictionarySize( 0 )
{
}
};
struct CustomFonts
{
QString standard;
QString serif;
QString sansSerif;
QString monospace;
bool operator==( CustomFonts const & other ) const
{
return standard == other.standard && serif == other.serif && sansSerif == other.sansSerif
&& monospace == other.monospace;
}
bool operator!=( CustomFonts const & other ) const
{
return !operator==( other );
}
QDomElement toElement( QDomDocument dd ) const
{
QDomElement proxy = dd.createElement( "customFonts" );
QDomAttr standard_attr = dd.createAttribute( "standard" );
standard_attr.setValue( this->standard );
proxy.setAttributeNode( standard_attr );
QDomAttr serif_attr = dd.createAttribute( "serif" );
serif_attr.setValue( this->serif );
proxy.setAttributeNode( serif_attr );
QDomAttr sans_attr = dd.createAttribute( "sans-serif" );
sans_attr.setValue( this->sansSerif );
proxy.setAttributeNode( sans_attr );
QDomAttr mono_attr = dd.createAttribute( "monospace" );
mono_attr.setValue( this->monospace );
proxy.setAttributeNode( mono_attr );
return proxy;
}
static CustomFonts fromElement( QDomElement proxy )
{
CustomFonts c;
c.standard = proxy.attribute( "standard" );
c.serif = proxy.attribute( "serif" );
c.sansSerif = proxy.attribute( "sans-serif" );
c.monospace = proxy.attribute( "monospace" );
return c;
}
};
/// This class encapsulates supported backend preprocessor logic,
/// discourages duplicating backend names in code, which is error-prone.
class InternalPlayerBackend
{
public:
/// Returns true if at least one backend is available.
static bool anyAvailable();
/// Returns the default backend or null backend if none is available.
static InternalPlayerBackend defaultBackend();
/// Returns the name list of supported backends.
static QStringList nameList();
/// Returns true if built with FFmpeg player support and the name matches.
bool isFfmpeg() const;
/// Returns true if built with Qt Multimedia player support and the name matches.
bool isQtmultimedia() const;
QString const & uiName() const
{
return name;
}
void setUiName( QString const & name_ )
{
name = name_;
}
bool operator==( InternalPlayerBackend const & other ) const
{
return name == other.name;
}
bool operator!=( InternalPlayerBackend const & other ) const
{
return !operator==( other );
}
private:
#ifdef MAKE_FFMPEG_PLAYER
static InternalPlayerBackend ffmpeg()
{
return InternalPlayerBackend( "FFmpeg" );
}
#endif
#ifdef MAKE_QTMULTIMEDIA_PLAYER
static InternalPlayerBackend qtmultimedia()
{
return InternalPlayerBackend( "Qt Multimedia" );
}
#endif
explicit InternalPlayerBackend( QString const & name_ ):
name( name_ )
{
}
QString name;
};
/// Various user preferences
struct Preferences
{
QString interfaceLanguage; // Empty value corresponds to system default
QString interfaceFont; //Empty as default value.
CustomFonts customFonts;
bool newTabsOpenAfterCurrentOne;
bool newTabsOpenInBackground;
bool hideSingleTab;
bool mruTabOrder;
bool hideMenubar;
bool enableTrayIcon;
bool startToTray;
bool closeToTray;
bool autoStart;
bool doubleClickTranslates;
bool selectWordBySingleClick;
bool autoScrollToTargetArticle;
bool escKeyHidesMainWindow;
bool alwaysOnTop;
/// An old UI mode when tranlateLine and wordList
/// are in the dockable side panel, not on the toolbar.
bool searchInDock;
bool enableMainWindowHotkey;
bool enableClipboardHotkey;
QKeySequence mainWindowHotkey;
QKeySequence clipboardHotkey;
bool startWithScanPopupOn;
bool enableScanPopupModifiers;
unsigned long scanPopupModifiers; // Combination of KeyboardState::Modifier
bool ignoreOwnClipboardChanges;
bool scanToMainWindow;
bool ignoreDiacritics;
bool ignorePunctuation;
bool sessionCollapse = false;
#ifdef HAVE_X11
bool trackClipboardScan;
bool trackSelectionScan;
bool showScanFlag;
int selectionChangeDelayTimer;
#endif
// Whether the word should be pronounced on page load, in main window/popup
bool pronounceOnLoadMain, pronounceOnLoadPopup;
bool useInternalPlayer;
InternalPlayerBackend internalPlayerBackend;
QString audioPlaybackProgram;
ProxyServer proxyServer;
AnkiConnectServer ankiConnectServer;
bool checkForNewReleases;
bool disallowContentFromOtherSites;
bool hideGoldenDictHeader;
int maxNetworkCacheSize;
bool clearNetworkCacheOnExit;
bool removeInvalidIndexOnExit = false;
qreal zoomFactor;
qreal helpZoomFactor;
int wordsZoomLevel;
unsigned maxStringsInHistory;
unsigned storeHistory;
bool alwaysExpandOptionalParts;
unsigned historyStoreInterval;
unsigned favoritesStoreInterval;
bool confirmFavoritesDeletion;
bool collapseBigArticles;
int articleSizeLimit;
bool limitInputPhraseLength;
int inputPhraseLengthLimit;
QString sanitizeInputPhrase( QString const & inputWord ) const;
unsigned short maxDictionaryRefsInContextMenu;
bool synonymSearchEnabled;
bool stripClipboard;
bool raiseWindowOnSearch;
FullTextSearch fts;
// Appearances
bool darkMode;
bool darkReaderMode;
QString addonStyle;
QString displayStyle; // Article Display style (Which also affect interface style on windows)
#if !defined( Q_OS_WIN )
// QApplication style https://doc.qt.io/qt-6/qapplication.html#setStyle
// In addition to Qt's styles, "Default" is added as default.
QString interfaceStyle;
#endif
Preferences();
};
/// A MediaWiki network dictionary definition
struct MediaWiki
{
QString id, name, url;
bool enabled;
QString icon;
QString lang;
MediaWiki():
enabled( false )
{
}
MediaWiki( QString const & id_,
QString const & name_,
QString const & url_,
bool enabled_,
QString const & icon_,
QString const & lang_ = "" ):
id( id_ ),
name( name_ ),
url( url_ ),
enabled( enabled_ ),
icon( icon_ ),
lang( lang_ )
{
}
bool operator==( MediaWiki const & other ) const
{
return id == other.id && name == other.name && url == other.url && enabled == other.enabled && icon == other.icon
&& lang == other.lang;
}
};
/// Any website which can be queried though a simple template substitution
struct WebSite
{
QString id, name, url;
bool enabled;
QString iconFilename;
bool inside_iframe;
WebSite():
enabled( false )
{
}
WebSite( QString const & id_,
QString const & name_,
QString const & url_,
bool enabled_,
QString const & iconFilename_,
bool inside_iframe_ ):
id( id_ ),
name( name_ ),
url( url_ ),
enabled( enabled_ ),
iconFilename( iconFilename_ ),
inside_iframe( inside_iframe_ )
{
}
bool operator==( WebSite const & other ) const
{
return id == other.id && name == other.name && url == other.url && enabled == other.enabled
&& iconFilename == other.iconFilename && inside_iframe == other.inside_iframe;
}
};
/// All the WebSites
typedef QList< WebSite > WebSites;
/// Any DICT server
struct DictServer
{
QString id, name, url;
bool enabled;
QString databases;
QString strategies;
QString iconFilename;
DictServer():
enabled( false )
{
}
DictServer( QString const & id_,
QString const & name_,
QString const & url_,
bool enabled_,
QString const & databases_,
QString const & strategies_,
QString const & iconFilename_ ):
id( id_ ),
name( name_ ),
url( url_ ),
enabled( enabled_ ),
databases( databases_ ),
strategies( strategies_ ),
iconFilename( iconFilename_ )
{
}
bool operator==( DictServer const & other ) const
{
return id == other.id && name == other.name && url == other.url && enabled == other.enabled
&& databases == other.databases && strategies == other.strategies && iconFilename == other.iconFilename;
}
};
/// All the DictServers
typedef QList< DictServer > DictServers;
/// Hunspell configuration
struct Hunspell
{
QString dictionariesPath;
typedef QList< QString > Dictionaries;
Dictionaries enabledDictionaries;
bool operator==( Hunspell const & other ) const
{
return dictionariesPath == other.dictionariesPath && enabledDictionaries == other.enabledDictionaries;
}
bool operator!=( Hunspell const & other ) const
{
return !operator==( other );
}
};
/// All the MediaWikis
typedef QList< MediaWiki > MediaWikis;
/// Chinese transliteration configuration
struct Chinese
{
bool enable;
bool enableSCToTWConversion;
bool enableSCToHKConversion;
bool enableTCToSCConversion;
Chinese();
bool operator==( Chinese const & other ) const
{
return enable == other.enable && enableSCToTWConversion == other.enableSCToTWConversion
&& enableSCToHKConversion == other.enableSCToHKConversion
&& enableTCToSCConversion == other.enableTCToSCConversion;
}
bool operator!=( Chinese const & other ) const
{
return !operator==( other );
}
};
struct CustomTrans
{
bool enable = false;
QString context;
bool operator==( CustomTrans const & other ) const
{
return enable == other.enable && context == other.context;
}
bool operator!=( CustomTrans const & other ) const
{
return !operator==( other );
}
};
/// Romaji transliteration configuration
struct Romaji
{
bool enable;
bool enableHepburn;
bool enableNihonShiki;
bool enableKunreiShiki;
bool enableHiragana;
bool enableKatakana;
Romaji();
bool operator==( Romaji const & other ) const
{
return enable == other.enable && enableHepburn == other.enableHepburn && enableNihonShiki == other.enableNihonShiki
&& enableKunreiShiki == other.enableKunreiShiki && enableHiragana == other.enableHiragana
&& enableKatakana == other.enableKatakana;
}
bool operator!=( Romaji const & other ) const
{
return !operator==( other );
}
};
struct Transliteration
{
bool enableRussianTransliteration;
bool enableGermanTransliteration;
bool enableGreekTransliteration;
bool enableBelarusianTransliteration;
CustomTrans customTrans;
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
Chinese chinese;
#endif
Romaji romaji;
bool operator==( Transliteration const & other ) const
{
return enableRussianTransliteration == other.enableRussianTransliteration
&& enableGermanTransliteration == other.enableGermanTransliteration
&& enableGreekTransliteration == other.enableGreekTransliteration
&& enableBelarusianTransliteration == other.enableBelarusianTransliteration && customTrans == other.customTrans &&
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
chinese == other.chinese &&
#endif
romaji == other.romaji;
}
bool operator!=( Transliteration const & other ) const
{
return !operator==( other );
}
Transliteration():
enableRussianTransliteration( false ),
enableGermanTransliteration( false ),
enableGreekTransliteration( false ),
enableBelarusianTransliteration( false )
{
}
};
struct Lingua
{
bool enable;
QString languageCodes;
bool operator==( Lingua const & other ) const
{
return enable == other.enable && languageCodes == other.languageCodes;
}
bool operator!=( Lingua const & other ) const
{
return !operator==( other );
}
};
struct Forvo
{
bool enable;
QString apiKey;
QString languageCodes;
Forvo():
enable( false )
{
}
bool operator==( Forvo const & other ) const
{
return enable == other.enable && apiKey == other.apiKey && languageCodes == other.languageCodes;
}
bool operator!=( Forvo const & other ) const
{
return !operator==( other );
}
};
struct Program
{
bool enabled;
enum Type {
Audio,
PlainText,
Html,
PrefixMatch,
MaxTypeValue
} type;
QString id, name, commandLine;
QString iconFilename;
Program():
enabled( false )
{
}
Program( bool enabled_,
Type type_,
QString const & id_,
QString const & name_,
QString const & commandLine_,
QString const & iconFilename_ ):
enabled( enabled_ ),
type( type_ ),
id( id_ ),
name( name_ ),
commandLine( commandLine_ ),
iconFilename( iconFilename_ )
{
}
bool operator==( Program const & other ) const
{
return enabled == other.enabled && type == other.type && name == other.name && commandLine == other.commandLine
&& iconFilename == other.iconFilename;
}
bool operator!=( Program const & other ) const
{
return !operator==( other );
}
};
typedef QList< Program > Programs;
#ifndef NO_TTS_SUPPORT
struct VoiceEngine
{
bool enabled;
//engine name.
QString engine_name;
QString name;
//voice name.
QString voice_name;
QString iconFilename;
QLocale locale;
int volume; // 0~1 allowed
int rate; // -1 ~ 1 allowed
VoiceEngine():
enabled( false ),
volume( 50 ),
rate( 0 )
{
}
VoiceEngine( QString engine_nane_, QString name_, QString voice_name_, QLocale locale_, int volume_, int rate_ ):
enabled( false ),
engine_name( engine_nane_ ),
name( name_ ),
voice_name( voice_name_ ),
locale( locale_ ),
volume( volume_ ),
rate( rate_ )
{
}
bool operator==( VoiceEngine const & other ) const
{
return enabled == other.enabled && engine_name == other.engine_name && name == other.name
&& voice_name == other.voice_name && locale == other.locale && iconFilename == other.iconFilename
&& volume == other.volume && rate == other.rate;
}
bool operator!=( VoiceEngine const & other ) const
{
return !operator==( other );
}
};
typedef QList< VoiceEngine > VoiceEngines;
#endif
struct HeadwordsDialog
{
int searchMode;
bool matchCase;
bool autoApply;
QString headwordsExportPath;
QByteArray headwordsDialogGeometry;
HeadwordsDialog():
searchMode( 0 ),
matchCase( false ),
autoApply( false )
{
}
};
struct Class
{
Paths paths;
SoundDirs soundDirs;
Group dictionaryOrder;
Group inactiveDictionaries;
Groups groups;
Preferences preferences;
MediaWikis mediawikis;
WebSites webSites;
DictServers dictServers;
Hunspell hunspell;
Transliteration transliteration;
Lingua lingua;
Forvo forvo;
Programs programs;
#ifndef NO_TTS_SUPPORT
VoiceEngines voiceEngines;
#endif
unsigned lastMainGroupId; // Last used group in main window
unsigned lastPopupGroupId; // Last used group in popup window
QByteArray popupWindowState; // Binary state saved by QMainWindow
QByteArray popupWindowGeometry; // Geometry saved by QMainWindow
QByteArray dictInfoGeometry; // Geometry of "Dictionary info" window
QByteArray inspectorGeometry; // Geometry of Web Engine inspector window
QByteArray dictionariesDialogGeometry; // Geometry of Dictionaries dialog
QString historyExportPath; // Path for export/import history
QString resourceSavePath; // Path to save images/audio
QString articleSavePath; // Path to save articles
bool pinPopupWindow; // Last pin status
bool popupWindowAlwaysOnTop; // Last status of pinned popup window
QByteArray mainWindowState; // Binary state saved by QMainWindow
QByteArray mainWindowGeometry; // Geometry saved by QMainWindow
MutedDictionaries mutedDictionaries; // Disabled via dictionary bar
MutedDictionaries popupMutedDictionaries; // Disabled via dictionary bar in popup
QDateTime timeForNewReleaseCheck; // Last time when the release was checked.
QString skippedRelease; // Empty by default
bool showingDictBarNames;
bool usingSmallIconsInToolbars;
/// Maximum size for the headwords.
/// Bigger headwords won't be indexed. For now, only in DSL.
unsigned int maxHeadwordSize;
unsigned int maxHeadwordsToExpand;
HeadwordsDialog headwordsDialog;
QString editDictionaryCommandLine; // Command line to call external editor for dictionary
Class():
lastMainGroupId( 0 ),
lastPopupGroupId( 0 ),
pinPopupWindow( false ),
showingDictBarNames( false ),
usingSmallIconsInToolbars( false ),
maxHeadwordSize( 256U ),
maxHeadwordsToExpand( 0 )
{
}
Group * getGroup( unsigned id );
Group const * getGroup( unsigned id ) const;
//disable tts dictionary. does not need to save to persistent file
bool notts = false;
bool resetState;
};
/// Configuration-specific events. Some parts of the program need to react
/// to specific changes in configuration. The object of this class is used
/// to emit signals when such events happen -- and the listeners connect to
/// them to be notified of them.
/// This class is separate from the main Class since QObjects can't be copied.
class Events: public QObject
{
Q_OBJECT
public:
/// Signals that the value of the mutedDictionaries has changed.
/// This emits mutedDictionariesChanged() signal, so the subscribers will
/// be notified.
void signalMutedDictionariesChanged();
signals:
/// THe value of the mutedDictionaries has changed.
void mutedDictionariesChanged();
private:
};
DEF_EX( exError, "Error with the program's configuration", std::exception )
DEF_EX( exCantUseDataDir, "Can't use XDG_DATA_HOME directory to store GoldenDict data", exError )
DEF_EX( exCantUseHomeDir, "Can't use home directory to store GoldenDict preferences", exError )
DEF_EX( exCantUseIndexDir, "Can't use index directory to store GoldenDict index files", exError )
DEF_EX( exCantReadConfigFile, "Can't read the configuration file", exError )
DEF_EX( exCantWriteConfigFile, "Can't write the configuration file", exError )
DEF_EX( exMalformedConfigFile, "The configuration file is malformed", exError )
/// Loads the configuration, or creates the default one if none is present
Class load();
/// Saves the configuration
void save( Class const & );
/// Returns the configuration file name.
QString getConfigFileName();
/// Returns the main configuration directory.
QString getConfigDir();
/// Returns the index directory, where the indices are to be stored.
QString getIndexDir();
/// Returns the filename of a .pid file which should store current pid of
/// the process.
QString getPidFileName();
/// Returns the filename of a history file which stores search history.
QString getHistoryFileName();
/// Returns the filename of a favorities file.
QString getFavoritiesFileName();
/// Returns the user .css file name (article-style.css).
QString getUserCssFileName();
/// Returns the user .js file name (article-script.js).
std::optional< std::string > getUserJsFileName();
/// Returns the user .css file name used for printing only.
QString getUserCssPrintFileName();
/// Returns the user .css file name for the Qt interface customization.
QString getUserQtCssFileName();
/// Returns the program's data dir. Under Linux that would be something like
/// /usr/share/apps/goldendict, under Windows C:/Program Files/GoldenDict.
QString getProgramDataDir() noexcept;
/// Returns the directory storing program localizized files (.qm).
QString getLocDir() noexcept;
/// Returns the directory storing program help files (.qch).
QString getHelpDir() noexcept;
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
/// Returns the directory storing OpenCC configuration and dictionary files (.json and .ocd).
QString getOpenCCDir() noexcept;
#endif
/// Returns true if the program is configured as a portable version. In that
/// mode, all the settings and indices are kept in the program's directory.
bool isPortableVersion() noexcept;
/// Returns directory with dictionaries for portable version. It is content/
/// in the application's directory.
QString getPortableVersionDictionaryDir() noexcept;
/// Returns directory with morpgologies for portable version. It is
/// content/morphology in the application's directory.
QString getPortableVersionMorphoDir() noexcept;
/// Returns the add-on styles directory.
QString getStylesDir();
/// Returns the directory where user-specific non-essential (cached) data should be written.
QString getCacheDir() noexcept;
} // namespace Config
#endif