diff --git a/build/depends.py b/build/depends.py index c9e8ec0e0122..177de02ce089 100644 --- a/build/depends.py +++ b/build/depends.py @@ -970,6 +970,7 @@ def sources(self, build): "util/statmodel.cpp", "util/time.cpp", "util/timer.cpp", + "util/config-parser.cpp", "util/performancetimer.cpp", "util/threadcputimer.cpp", "util/version.cpp", diff --git a/src/mixxx.cpp b/src/mixxx.cpp index 30e9838e7ca8..84bf1f518ed7 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -67,6 +67,7 @@ #include "util/statsmanager.h" #include "util/timer.h" #include "util/time.h" +#include "util/config-parser.h" #include "util/version.h" #include "controlpushbutton.h" #include "util/compatibility.h" @@ -124,6 +125,13 @@ MixxxMainWindow::MixxxMainWindow(QApplication* pApp, const CmdlineArgs& args) // after an upgrade and make any needed changes. m_pUpgrader = new Upgrade; m_pConfig = m_pUpgrader->versionUpgrade(args.getSettingsPath()); + + registerConfigPath(m_pConfig->getSettingsPath() + "/mixxx_qsettings_temp.cfg"); + UserSettings user_settings; + // I need to explicitely ask for a reference because QSettings doesn't + // support move in Qt4 + auto& qsettings = user_settings.getQSettings(); + ControlDoublePrivate::setUserConfig(m_pConfig); // First load launch image to show a the user a quick responds diff --git a/src/test/config_parser_test.cpp b/src/test/config_parser_test.cpp new file mode 100644 index 000000000000..943e7c60a274 --- /dev/null +++ b/src/test/config_parser_test.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +#include "test/mixxxtest.h" + +#include "util/config-parser.h" + +const QString kConfigLocation(QDir::currentPath() + + "/src/test/settings-dummy-data/mixxx.cfg"); + +TEST(QSETTINGS, readConfigtest) { + QSettings::SettingsMap map; + QFile configFile(kConfigLocation); + EXPECT_TRUE(configFile.open(QIODevice::ReadOnly)); + EXPECT_TRUE(readConfig(configFile, map)); + EXPECT_EQ(265, map.size()); +} + +TEST(QSETTINGS, writeConfigtest) { + QSettings::SettingsMap map; + QFile configFile(kConfigLocation); + EXPECT_TRUE(configFile.open(QIODevice::ReadOnly)); + EXPECT_TRUE(readConfig(configFile, map)); + + QTemporaryFile saveFile; + saveFile.open(); + writeConfig(saveFile, map); + saveFile.close(); + + auto map2 = QSettings::SettingsMap(); + saveFile.open(); + readConfig(saveFile, map2); + + EXPECT_EQ(map.size(), map2.size()); + + for (const auto& key : map.keys()) { + EXPECT_QSTRING_EQ(map[key].toString(), map2[key].toString()); + } +} diff --git a/src/test/settings-dummy-data/mixxx.cfg b/src/test/settings-dummy-data/mixxx.cfg new file mode 100644 index 000000000000..d844723a468f --- /dev/null +++ b/src/test/settings-dummy-data/mixxx.cfg @@ -0,0 +1,507 @@ + +[Config] +Version 1.12.0-beta1 +Path /home/max/foss/mixxx-dev/mixxx/res/ +Locale + +[Controls] +Tooltips 1 + +[Keyboard] +Enabled 1 + +[Recording] +FileSize + +[Playlist] +Directory /home/max/Music + +[Library] +WriteAudioTags 0 + +[Channel1] +vinylcontrol_speed_type 33.3 RPM + +[Controls] +RateRamp +RateRampSensitivity 100 + +[Channel2] +vinylcontrol_speed_type 33.3 RPM + +[Sampler1] +vinylcontrol_speed_type + +[Sampler2] +vinylcontrol_speed_type + +[Sampler3] +vinylcontrol_speed_type + +[Sampler4] +vinylcontrol_speed_type + +[PreviewDeck1] +vinylcontrol_speed_type + +[VinylControl] +mode 0 +cueing_ch1 0 +cueing_ch2 0 + +[Browse] +QuickLinks + +[Library] +ShowITunesLibrary 1 + +[BPM] +BPMRangeStart 70 +BPMRangeEnd 140 +AnalyzeEntireSong 1 + +[Waveform] +FrameRate 30 +DefaultZoom 3 +ZoomSynchronization 0 +WaveformType 6 +VisualGain_0 1.5 +VisualGain_1 1 +VisualGain_2 1 +VisualGain_3 1 +OverviewNormalized 0 + +[Promo] +StatTracking 0 + +[Library] +RescanOnStartup 0 +UseRelativePathOnExport 0 +ShowRhythmboxLibrary 1 +ShowTraktorLibrary 1 + +[Controls] +PositionDisplay 0 +RateDir 0 +RateRange 2 +RateTempLeft 4.0 +RateTempRight 2.000000 +RatePermLeft 0.50 +RatePermRight 0.05 +AllowTrackLoadToPlayingDeck +CueDefault 0 +CueRecall + +[Auto DJ] +Requeue 0 + +[Config] +Skin LateNight1280x800-WXGA + +[Mixer Profile] +HiEQFrequency 2484 +HiEQFrequencyPrecise 2484.999990 +LoEQFrequency 246 +LoEQFrequencyPrecise 246.469196 +LoFiEQs yes +xFaderCurve 1 +xFaderMode 0 +xFaderReverse 0 + +[Vamp] +AnalyserBeatPluginID qm-tempotracker:0 +AnalyserBeatLibrary libmixxxminimal + +[BPM] +BPMDetectionEnabled 1 +BeatDetectionFixedTempoAssumption 1 +FixedTempoOffsetCorrection 1 +ReanalyzeWhenSettingsChange 0 +FastAnalysisEnabled 0 + +[ReplayGain] +ReplayGainEnabled 1 +ReplayGainAnalyserEnabled 1 +InitialReplayGainBoost 0 + +[Recording] +Directory /home/max/Music/Mixxx/Recordings +Encoding WAV +Title +Author +Album +CueEnabled + +[Shoutcast] +enabled 0 +servertype Icecast2 +mountpoint +host +port 8000 +login +password +stream_name +stream_website http://www.mixxx.org +stream_desc This stream is online for testing purposes! +stream_genre Live Mix +stream_public 0 +ogg_dynamicupdate 0 +bitrate 128 +format MP3 +channels 2 +metadata_charset +enable_metadata 0 +custom_artist +custom_title + +[Samplers] +show_samplers 0 + +[PreviewDeck] +show_previewdeck 1 + +[Library] +VScrollBarPos 0 + +[Auto DJ] +Transition 10 + +[Vinylcontrol] +show_vinylcontrol 0 + +[Microphone] +show_microphone 0 + +[Spinny1] +show_spinny 1 + +[Spinny2] +show_spinny 1 + +[Library] +SupportedFileExtensions aif,aiff,flac,mp3,ogg,wav + +[Channel1] +vinylcontrol_vinyl_type Serato CV02 Vinyl, Side A + +[Channel2] +vinylcontrol_vinyl_type Serato CV02 Vinyl, Side A + +[VinylControl] +lead_in_time 0 +needle_skip_prevention 0 +show_signal_quality 0 +gain 1 + +[BPM] +BPMAboveRangeEnabled + +[Channel 1] +vinylcontrol_enabled 0 + +[Channel 2] +vinylcontrol_enabled 0 + +[ControllerPreset] +DJ_Control_Air_MIDI_1 /home/max/.mixxx/controllers/DJ_Control_Air_MIDI_1.midi.xml + +[Controller] +DJ_Control_Air_MIDI_1 1 + +[Soundcard] +Samplerate 44100 + +[InternalClock] +bpm 133 + +[Master] +delay 0 +headDelay 0 +duckStrength 90 +duckMode 1 +keylock_engine 0 +enabled 1 +mono_mixdown 0 +talkover_mix 0 + +[Library] +ShowBansheeLibrary 1 + +[Banshee] +Database + +[Library] +RowHeight 21 +Font + +[Waveform] +EndOfTrackWarningTime 30 +VSync 0 + +[Library] +TrackLoadAction 0 + +[Controls] +keylockMode + +[Config] +ResizableSkin Deere +StartInFullscreen + +[Controls] +SpeedAutoReset 1 + +[Waveform] +WaveformOverviewType + +[Auto DJ] +MinimumAvailable 20 +UseIgnoreTime 0 +IgnoreTime 23:59 +EnableRandomQueue 0 +RandomQueueMinimumAllowed 5 +EnableRandomQueueBuff 0 + +[Mixer Profile] +EQsOnly yes +SingleEQEffect yes +EffectForGroup_[Master] none +EqAutoReset 0 +EnableEQs + +[Vamp] +AnalyserKeyPluginID qm-keydetector:2 +AnalyserKeyLibrary libmixxxminimal + +[Key] +KeyDetectionEnabled 1 +FastAnalysisEnabled 0 +ReanalyzeWhenSettingsChange 0 +KeyNotation Traditional + +[ReplayGain] +InitialDefaultBoost -6 + +[Shoutcast] +metadata_format $artist - $title + +[ScriptDebugger] +Enabled + +[Channel3] +vinylcontrol_speed_type 33.3 RPM + +[Channel4] +vinylcontrol_speed_type 33.3 RPM + +[VinylControl] +mode_ch1 1 +mode_ch2 1 +cueing_ch3 0 +mode_ch3 1 +cueing_ch4 0 +mode_ch4 1 + +[Mixer Profile] +EffectForGroup_[Channel1] org.mixxx.effects.bessel8lvmixeq +QuickEffectForGroup_[Channel1] org.mixxx.effects.filter +EffectForGroup_[Channel2] org.mixxx.effects.bessel8lvmixeq +QuickEffectForGroup_[Channel2] org.mixxx.effects.filter +EffectForGroup_[Channel3] org.mixxx.effects.bessel8lvmixeq +QuickEffectForGroup_[Channel3] org.mixxx.effects.filter +EffectForGroup_[Channel4] org.mixxx.effects.bessel8lvmixeq +QuickEffectForGroup_[Channel4] org.mixxx.effects.filter + +[Spinny3] +show_spinny 1 + +[Spinny4] +show_spinny 1 + +[MicrophoneRack] +show 0 + +[EffectRack1] +show 1 + +[Master] +show_mixer 1 +show_eqs 0 +show_mics 0 +view_curpage 2 + +[Library] +show_coverart 0 +show_current 1 + +[Master] +show_4decks 0 + +[Spinny] +show_spinnies 0 + +[VinylControl] +show_vinylcontrol 0 + +[Preferences] +geometry + +[Channel3] +vinylcontrol_vinyl_type Serato CV02 Vinyl, Side A + +[Channel4] +vinylcontrol_vinyl_type Serato CV02 Vinyl, Side A + +[Channel1] +vinylcontrol_lead_in_time 0 + +[Channel2] +vinylcontrol_lead_in_time 0 + +[Channel3] +vinylcontrol_lead_in_time 0 + +[Channel4] +vinylcontrol_lead_in_time 0 + +[Controls] +PitchAndKeylockMode 0 + +[Auto DJ] +RequeueBuff +MinimumAvailableBuff +IgnoreTimeBuff +UseIgnoreTimeBuff +RandomQueueMinimumAllowedBuff + +[Channel1] +vinylcontrol_enabled 0 + +[Channel2] +vinylcontrol_enabled 0 + +[Channel3] +vinylcontrol_enabled 0 + +[Channel4] +vinylcontrol_enabled 0 + +[Controls] +RateRangePercent 10 + +[Master] +mixer_4decks_status 0 +spinny_size 0 + +[Sampler5] +vinylcontrol_speed_type + +[Sampler6] +vinylcontrol_speed_type + +[Sampler7] +vinylcontrol_speed_type + +[Sampler8] +vinylcontrol_speed_type + +[Master] +hide_mixer 0 + +[Channel1] +keylock 0 +quantize 0 + +[Channel2] +keylock 0 +quantize 0 + +[Channel3] +keylock 0 +quantize 0 + +[Channel4] +keylock 0 +quantize 0 + +[Sampler1] +keylock 0 +quantize 0 + +[Sampler2] +keylock 0 +quantize 0 + +[Sampler3] +keylock 0 +quantize 0 + +[Sampler4] +keylock 0 +quantize 0 + +[PreviewDeck1] +keylock 0 +quantize 0 + +[Sampler5] +keylock 0 +quantize 0 + +[Sampler6] +keylock 0 +quantize 0 + +[Sampler7] +keylock 0 +quantize 0 + +[Sampler8] +keylock 0 +quantize 0 + +[Sampler9] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler10] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler11] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler12] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler13] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler14] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler15] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Sampler16] +keylock 0 +quantize 0 +vinylcontrol_speed_type + +[Deere] +show_stacked_waveforms 0 +show_starrating 0 +show_more_hotcues 0 +sampler_bank_current 0 diff --git a/src/util/config-parser.cpp b/src/util/config-parser.cpp new file mode 100644 index 000000000000..1194d093dad1 --- /dev/null +++ b/src/util/config-parser.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +#include "util/config-parser.h" + +bool readConfig(QIODevice &device, QSettings::SettingsMap &map) { + bool found_group = false; + QString groupStr, line; + QTextStream configfile(&device); + configfile.setCodec("UTF-8"); + + while (!configfile.atEnd()) { + line = configfile.readLine().trimmed(); + if (line.length() != 0) { + if (line.startsWith("[") && line.endsWith("]")) { + found_group = true; + groupStr = line; + groupStr.remove(QRegExp("[\\[\\]]")); + } else if (found_group) { + QString key, val; + QTextStream(&line) >> key >> val; + map.insert(groupStr + "/" + key, QVariant(val)); + } + } + } + + return true; +} + +bool writeConfig(QIODevice &device, const QSettings::SettingsMap &map) { + QTextStream configfile(&device); + configfile.setCodec("UTF-8"); + + QString grp = ""; + // can't simply use c++11 for-each loop see + // http://stackoverflow.com/a/8529237/2207958 + for (auto el = map.begin(), end = map.end(); el != end; ++el) { + QStringList group_key = el.key().split("/"); + QString group = "[" + group_key.at(0) + "]"; + QString key = group_key.at(1); + QString val = el.value().toString(); + if (group != grp) { + grp = group; + configfile << "\n" << group << "\n"; + } + configfile << key << " " << val << "\n"; + } + + return true; +} + +QString UserSettings::m_config_fname = "NONE"; +QSettings::Format UserSettings::m_format = + QSettings::registerFormat("cfg", readConfig, writeConfig); + +void registerConfigPath(const QString &config_path) { + UserSettings::m_config_fname = config_path; +} diff --git a/src/util/config-parser.h b/src/util/config-parser.h new file mode 100644 index 000000000000..333551f6aad0 --- /dev/null +++ b/src/util/config-parser.h @@ -0,0 +1,37 @@ +#ifndef CONFIG_PARSER_H +#define CONFIG_PARSER_H + +#include +#include + +bool readConfig(QIODevice& device, QSettings::SettingsMap& map); +bool writeConfig(QIODevice& device, const QSettings::SettingsMap& map); + +class UserSettings { + public: + UserSettings() : m_settings(m_config_fname, m_format) { + if (m_config_fname == "NONE") { + // throw + } + m_settings.setFallbacksEnabled(false); + } + + // prevent moving + UserSettings(UserSettings&& rhs) = delete; + UserSettings& operator=(UserSettings&& rhs) = delete; + // prevent copying + UserSettings(const UserSettings& rhs) = delete; + UserSettings& operator=(const UserSettings& rhs) = delete; + + QSettings& getQSettings() { return m_settings; } + + static QString m_config_fname; + static QSettings::Format m_format; + + private: + QSettings m_settings; +}; + +void registerConfigPath(const QString& config_path); + +#endif // CONFIG_PARSER_H