File indexing completed on 2024-05-12 17:22:41
0001 // This file is part of the SpeedCrunch project 0002 // Copyright (C) 2004, 2005, 2007, 2008 Ariya Hidayat <ariya@kde.org> 0003 // Copyright (C) 2005-2006 Johan Thelin <e8johan@gmail.com> 0004 // Copyright (C) 2007-2016 @heldercorreia 0005 // Copyright (C) 2015 Pol Welter <polwelter@gmail.com> 0006 // 0007 // This program is free software; you can redistribute it and/or 0008 // modify it under the terms of the GNU General Public License 0009 // as published by the Free Software Foundation; either version 2 0010 // of the License, or (at your option) any later version. 0011 // 0012 // This program is distributed in the hope that it will be useful, 0013 // but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 // GNU General Public License for more details. 0016 // 0017 // You should have received a copy of the GNU General Public License 0018 // along with this program; see the file COPYING. If not, write to 0019 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 // Boston, MA 02110-1301, USA. 0021 0022 #include "settings.h" 0023 0024 #include "floatconfig.h" 0025 0026 #include <QDir> 0027 #include <QLocale> 0028 #include <QSettings> 0029 #include <QApplication> 0030 #include <QFont> 0031 #include <QtCore/QStandardPaths> 0032 0033 #ifdef Q_OS_WIN 0034 # define WIN32_LEAN_AND_MEAN 0035 # ifndef NOMINMAX 0036 # define NOMINMAX 0037 # endif 0038 # include <windows.h> 0039 # include <shlobj.h> 0040 #endif 0041 0042 0043 // The current config revision. Based on the application version number 0044 // ('1200' corresponds to 0.12.0, '10300' would be 1.3.0 etc.). When making 0045 // a backwards-incompatible change to the config format, bump this number to 0046 // the next release (if not already happened), then update the migration code 0047 // in createQSettings. Don't bump the config version unnecessarily for 0048 // releases that don't contain incompatible changes. 0049 static const int ConfigVersion = 1200; 0050 0051 0052 static const char* DefaultColorScheme = "Terminal"; 0053 0054 QString Settings::getConfigPath() 0055 { 0056 #ifdef SPEEDCRUNCH_PORTABLE 0057 return QApplication::applicationDirPath(); 0058 #elif defined(Q_OS_WIN) 0059 // On Windows, use AppData/Roaming/SpeedCrunch, the same path as getDataPath. 0060 return getDataPath(); 0061 #else 0062 // Everywhere else, use `QStandardPaths::ConfigLocation`/SpeedCrunch: 0063 // * OSX: ~/Library/Preferences/SpeedCrunch 0064 // * Linux: ~/.config/SpeedCrunch 0065 return QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), 0066 QCoreApplication::applicationName()); 0067 #endif 0068 } 0069 0070 QString Settings::getDataPath() 0071 { 0072 #ifdef SPEEDCRUNCH_PORTABLE 0073 return QApplication::applicationDirPath(); 0074 #elif QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) 0075 return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0076 #elif defined(Q_OS_WIN) 0077 // We can't use AppDataLocation, so we simply use the Win32 API to emulate it. 0078 WCHAR w32path[MAX_PATH]; 0079 HRESULT result = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, w32path); 0080 Q_ASSERT(SUCCEEDED(result)); 0081 QString path = QString::fromWCharArray(w32path); 0082 QString orgName = QCoreApplication::organizationName(); 0083 QString appName = QCoreApplication::applicationName(); 0084 if (!orgName.isEmpty()) { 0085 path.append('\\'); 0086 path.append(orgName); 0087 } 0088 if (!appName.isEmpty()) { 0089 path.append('\\'); 0090 path.append(appName); 0091 } 0092 return QDir::fromNativeSeparators(path); 0093 #else 0094 // Any non-Windows with Qt < 5.4. Since DataLocation and AppDataLocation are 0095 // equivalent outside of Windows, that should be fine. 0096 return QStandardPaths::writableLocation(QStandardPaths::DataLocation); 0097 #endif 0098 } 0099 0100 QString Settings::getCachePath() 0101 { 0102 #ifdef SPEEDCRUNCH_PORTABLE 0103 return QApplication::applicationDirPath(); 0104 #else 0105 return QStandardPaths::writableLocation(QStandardPaths::CacheLocation); 0106 #endif 0107 } 0108 0109 static Settings* s_settingsInstance = 0; 0110 static char s_radixCharacter = 0; 0111 0112 static void s_deleteSettings() 0113 { 0114 delete s_settingsInstance; 0115 } 0116 0117 static QSettings* createQSettings(const QString& key); 0118 0119 Settings* Settings::instance() 0120 { 0121 if (!s_settingsInstance) { 0122 s_settingsInstance = new Settings; 0123 s_settingsInstance->load(); 0124 qAddPostRoutine(s_deleteSettings); 0125 } 0126 0127 return s_settingsInstance; 0128 } 0129 0130 Settings::Settings() 0131 { 0132 } 0133 0134 void Settings::load() 0135 { 0136 const char* KEY = "SpeedCrunch"; 0137 0138 QSettings* settings = createQSettings(KEY); 0139 if (!settings) 0140 return; 0141 0142 QString key = QString::fromLatin1(KEY) + QLatin1String("/General/"); 0143 0144 // Angle mode special case. 0145 QString angleUnitStr; 0146 angleUnitStr = settings->value(key + QLatin1String("AngleMode"), "r").toString(); 0147 if (angleUnitStr != QLatin1String("r") && angleUnitStr != QLatin1String("d") && angleUnitStr != QLatin1String("g")) 0148 angleUnit = 'r'; 0149 else 0150 angleUnit = angleUnitStr.at(0).toLatin1(); 0151 0152 // Radix character special case. 0153 QString radixCharStr; 0154 radixCharStr = settings->value(key + QLatin1String("RadixCharacter"), "*").toString(); 0155 setRadixCharacter(radixCharStr.at(0).toLatin1()); 0156 0157 autoAns = settings->value(key + QLatin1String("AutoAns"), true).toBool(); 0158 autoCalc = settings->value(key + QLatin1String("AutoCalc"), true).toBool(); 0159 autoCompletion = settings->value(key + QLatin1String("AutoCompletion"), true).toBool(); 0160 sessionSave = settings->value(key + QLatin1String("SessionSave"), true).toBool(); 0161 leaveLastExpression = settings->value(key + QLatin1String("LeaveLastExpression"), false).toBool(); 0162 language = settings->value(key + QLatin1String("Language"), "C").toString(); 0163 syntaxHighlighting = settings->value(key + QLatin1String("SyntaxHighlighting"), true).toBool(); 0164 autoResultToClipboard = settings->value(key + QLatin1String("AutoResultToClipboard"), false).toBool(); 0165 windowPositionSave = settings->value(key + QLatin1String("WindowPositionSave"), true).toBool(); 0166 complexNumbers = settings->value(key + QLatin1String("ComplexNumbers"), false).toBool(); 0167 0168 digitGrouping = settings->value(key + QLatin1String("DigitGrouping"), 0).toInt(); 0169 digitGrouping = std::min(3, std::max(0, digitGrouping)); 0170 0171 key = KEY + QLatin1String("/Format/"); 0172 0173 // Format special case. 0174 QString format; 0175 format = settings->value(key + QLatin1String("Type"), 'f').toString(); 0176 if (format != "g" && format != "f" && format != "e" && format != "n"&& format != "h" && format != "o" && format != "b") 0177 resultFormat = 'f'; 0178 else 0179 resultFormat = format.at(0).toLatin1(); 0180 0181 // Complex format special case. 0182 QString cmplxFormat; 0183 cmplxFormat = settings->value(key + QLatin1String("ComplexForm"), 'c').toString(); 0184 if (cmplxFormat != "c" && cmplxFormat != "p") 0185 resultFormatComplex = 'c'; 0186 else 0187 resultFormatComplex = cmplxFormat.at(0).toLatin1(); 0188 0189 resultPrecision = settings->value(key + QLatin1String("Precision"), -1).toInt(); 0190 0191 if (resultPrecision > DECPRECISION) 0192 resultPrecision = DECPRECISION; 0193 0194 key = KEY + QLatin1String("/Layout/"); 0195 windowOnfullScreen = settings->value(key + QLatin1String("WindowOnFullScreen"), false).toBool(); 0196 historyDockVisible = settings->value(key + QLatin1String("HistoryDockVisible"), false).toBool(); 0197 keypadVisible = settings->value(key + QLatin1String("KeypadVisible"), false).toBool(); 0198 statusBarVisible = settings->value(key + QLatin1String("StatusBarVisible"), false).toBool(); 0199 functionsDockVisible = settings->value(key + QLatin1String("FunctionsDockVisible"), false).toBool(); 0200 variablesDockVisible = settings->value(key + QLatin1String("VariablesDockVisible"), false).toBool(); 0201 userFunctionsDockVisible = settings->value(key + QLatin1String("UserFunctionsDockVisible"), false).toBool(); 0202 formulaBookDockVisible = settings->value(key + QLatin1String("FormulaBookDockVisible"), false).toBool(); 0203 constantsDockVisible = settings->value(key + QLatin1String("ConstantsDockVisible"), false).toBool(); 0204 windowAlwaysOnTop = settings->value(key + QLatin1String("WindowAlwaysOnTop"), false).toBool(); 0205 bitfieldVisible = settings->value(key + QLatin1String("BitfieldVisible"), false).toBool(); 0206 0207 windowState = settings->value(key + QLatin1String("State")).toByteArray(); 0208 windowGeometry = settings->value(key + QLatin1String("WindowGeometry")).toByteArray(); 0209 manualWindowGeometry = settings->value(key + QLatin1String("ManualWindowGeometry")).toByteArray(); 0210 0211 key = KEY + QLatin1String("/Display/"); 0212 displayFont = settings->value(key + QLatin1String("DisplayFont"), QFont().toString()).toString(); 0213 colorScheme = settings->value(key + QLatin1String("ColorSchemeName"), DefaultColorScheme).toString(); 0214 0215 delete settings; 0216 } 0217 0218 void Settings::save() 0219 { 0220 const QString KEY = QString::fromLatin1("SpeedCrunch"); 0221 0222 QSettings* settings = createQSettings(KEY); 0223 if (!settings) 0224 return; 0225 0226 QString key = KEY + QLatin1String("/General/"); 0227 0228 settings->setValue(key + QLatin1String("SessionSave"), sessionSave); 0229 settings->setValue(key + QLatin1String("LeaveLastExpression"), leaveLastExpression); 0230 settings->setValue(key + QLatin1String("AutoCompletion"), autoCompletion); 0231 settings->setValue(key + QLatin1String("AutoAns"), autoAns); 0232 settings->setValue(key + QLatin1String("AutoCalc"), autoCalc); 0233 settings->setValue(key + QLatin1String("SyntaxHighlighting"), syntaxHighlighting); 0234 settings->setValue(key + QLatin1String("DigitGrouping"), digitGrouping); 0235 settings->setValue(key + QLatin1String("AutoResultToClipboard"), autoResultToClipboard); 0236 settings->setValue(key + QLatin1String("Language"), language); 0237 settings->setValue(key + QLatin1String("WindowPositionSave"), windowPositionSave); 0238 settings->setValue(key + QLatin1String("ComplexNumbers"), complexNumbers); 0239 0240 settings->setValue(key + QLatin1String("AngleMode"), QString(QChar(angleUnit))); 0241 0242 char c = 'C'; 0243 if (s_radixCharacter != 0) 0244 c = s_radixCharacter; 0245 settings->setValue(key + QLatin1String("RadixCharacter"), QString(QChar(c))); 0246 0247 key = KEY + QLatin1String("/Format/"); 0248 0249 settings->setValue(key + QLatin1String("Type"), QString(QChar(resultFormat))); 0250 settings->setValue(key + QLatin1String("ComplexForm"), QString(QChar(resultFormatComplex))); 0251 settings->setValue(key + QLatin1String("Precision"), resultPrecision); 0252 0253 key = KEY + QLatin1String("/Layout/"); 0254 0255 settings->setValue(key + QLatin1String("FormulaBookDockVisible"), formulaBookDockVisible); 0256 settings->setValue(key + QLatin1String("ConstantsDockVisible"), constantsDockVisible); 0257 settings->setValue(key + QLatin1String("FunctionsDockVisible"), functionsDockVisible); 0258 settings->setValue(key + QLatin1String("HistoryDockVisible"), historyDockVisible); 0259 settings->setValue(key + QLatin1String("WindowOnFullScreen"), windowOnfullScreen); 0260 settings->setValue(key + QLatin1String("KeypadVisible"), keypadVisible); 0261 settings->setValue(key + QLatin1String("StatusBarVisible"), statusBarVisible); 0262 settings->setValue(key + QLatin1String("VariablesDockVisible"), variablesDockVisible); 0263 settings->setValue(key + QLatin1String("UserFunctionsDockVisible"), userFunctionsDockVisible); 0264 settings->setValue(key + QLatin1String("WindowAlwaysOnTop"), windowAlwaysOnTop); 0265 settings->setValue(key + QLatin1String("State"), windowState); 0266 settings->setValue(key + QLatin1String("WindowGeometry"), windowGeometry); 0267 settings->setValue(key + QLatin1String("ManualWindowGeometry"), manualWindowGeometry); 0268 settings->setValue(key + QLatin1String("BitfieldVisible"), bitfieldVisible); 0269 0270 key = KEY + QLatin1String("/Display/"); 0271 0272 settings->setValue(key + QLatin1String("DisplayFont"), displayFont); 0273 settings->setValue(key + QLatin1String("ColorSchemeName"), colorScheme); 0274 0275 0276 delete settings; 0277 } 0278 0279 char Settings::radixCharacter() const 0280 { 0281 if (isRadixCharacterAuto() || isRadixCharacterBoth()) 0282 return QLocale().decimalPoint().toLatin1(); 0283 0284 return s_radixCharacter; 0285 } 0286 0287 bool Settings::isRadixCharacterAuto() const 0288 { 0289 return s_radixCharacter == 0; 0290 } 0291 0292 bool Settings::isRadixCharacterBoth() const 0293 { 0294 return s_radixCharacter == '*'; 0295 } 0296 0297 void Settings::setRadixCharacter(char c) 0298 { 0299 s_radixCharacter = (c != ',' && c != '.' && c != '*') ? 0 : c; 0300 } 0301 0302 0303 // Settings migration from legacy (0.11 and before) to 0.12 (ConfigVersion 1200). 0304 static void migrateSettings_legacyTo1200(QSettings* settings, const QString& KEY) 0305 { 0306 #ifdef SPEEDCRUNCH_PORTABLE 0307 // This is the same as the new path, but let's cut down 0308 QSettings* legacy = new QSettings(Settings::getConfigPath() + "/" + KEY + ".ini", QSettings::IniFormat); 0309 #else 0310 QSettings* legacy = new QSettings(QSettings::NativeFormat, QSettings::UserScope, KEY, KEY); 0311 #endif 0312 0313 QString legacyFile = legacy->fileName(); 0314 if (legacyFile != settings->fileName()) { 0315 // If the file names are different, we assume the legacy settings were in a 0316 // different location (most were, but e.g. not on the portable version) so we 0317 // copy everything over, then delete the old settings. On Windows, the file 0318 // name may also be a registry path, but the same reasoning applies. 0319 for (auto& key : legacy->allKeys()) 0320 settings->setValue(key, legacy->value(key)); 0321 0322 #ifdef Q_OS_WIN 0323 // On Windows, check if the legacy settings were in the registry; if so, just clear() 0324 // them. 0325 if (legacy->format() == QSettings::NativeFormat) { 0326 legacy->clear(); 0327 delete legacy; 0328 } else { 0329 delete legacy; 0330 QFile::remove(legacyFile); 0331 } 0332 #else 0333 // On other platforms, delete the legacy settings file. 0334 delete legacy; 0335 QFile::remove(legacyFile); 0336 #endif 0337 } else { 0338 // If both settings objects point to the same location, removing the old stuff 0339 // may well break the new settings. 0340 delete legacy; 0341 } 0342 0343 0344 // ColorScheme -> ColorSchemeName 0345 QString colorSchemeName; 0346 switch (settings->value("SpeedCrunch/Display/ColorScheme", -1).toInt()) { 0347 case 0: 0348 colorSchemeName = "Terminal"; 0349 break; 0350 case 1: 0351 colorSchemeName = "Standard"; 0352 break; 0353 case 2: 0354 colorSchemeName = "Sublime"; 0355 break; 0356 default: 0357 colorSchemeName = DefaultColorScheme; 0358 break; 0359 } 0360 settings->setValue("SpeedCrunch/Display/ColorSchemeName", colorSchemeName); 0361 settings->remove("SpeedCrunch/Display/ColorScheme"); 0362 0363 // DigitGrouping (bool) -> DigitGrouping (int) 0364 bool groupDigits = settings->value("SpeedCrunch/General/DigitGrouping", false).toBool(); 0365 settings->setValue("SpeedCrunch/General/DigitGrouping", groupDigits ? 1 : 0); 0366 } 0367 0368 0369 QSettings* createQSettings(const QString& KEY) 0370 { 0371 QSettings* settings = new QSettings(Settings::getConfigPath() + "/" + KEY + ".ini", QSettings::IniFormat); 0372 int ver = settings->value("ConfigVersion", 0).toInt(); 0373 switch (ver) { 0374 case 0: 0375 migrateSettings_legacyTo1200(settings, KEY); 0376 break; 0377 } 0378 settings->setValue("ConfigVersion", ConfigVersion); 0379 return settings; 0380 }